Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
1ad9cce30f |
574
Orangemart.cs
574
Orangemart.cs
@ -9,7 +9,7 @@ using Oxide.Core.Libraries;
|
||||
|
||||
namespace Oxide.Plugins
|
||||
{
|
||||
[Info("Orangemart", "saulteafarmer", "0.3.0")]
|
||||
[Info("Orangemart", "RustySats", "0.3.0")]
|
||||
[Description("Allows players to buy and sell in-game units and VIP status using Bitcoin Lightning Network payments via LNbits")]
|
||||
public class Orangemart : CovalencePlugin
|
||||
{
|
||||
@ -101,13 +101,21 @@ namespace Oxide.Plugins
|
||||
}
|
||||
|
||||
// Invoice and Payment Classes
|
||||
private class InvoiceResponse
|
||||
{
|
||||
[JsonProperty("payment_request")]
|
||||
public string PaymentRequest { get; set; }
|
||||
private class InvoiceResponse
|
||||
{
|
||||
[JsonProperty("bolt11")]
|
||||
public string PaymentRequest { get; set; }
|
||||
|
||||
[JsonProperty("payment_hash")]
|
||||
public string PaymentHash { get; set; }
|
||||
[JsonProperty("payment_hash")]
|
||||
public string PaymentHash { get; set; }
|
||||
}
|
||||
|
||||
|
||||
// NEW: Wrapper class for LNbits v1 responses
|
||||
private class InvoiceResponseWrapper
|
||||
{
|
||||
[JsonProperty("data")]
|
||||
public InvoiceResponse Data { get; set; }
|
||||
}
|
||||
|
||||
private class SellInvoiceLogEntry
|
||||
@ -434,6 +442,45 @@ namespace Oxide.Plugins
|
||||
return allItems;
|
||||
}
|
||||
|
||||
private bool IsCurrencyItem(Item item)
|
||||
{
|
||||
return item.info.itemid == currencyItemID && (currencySkinID == 0 || item.skin == currencySkinID);
|
||||
}
|
||||
|
||||
private bool TryReserveCurrency(BasePlayer player, int amount)
|
||||
{
|
||||
var items = GetAllInventoryItems(player).Where(IsCurrencyItem).ToList();
|
||||
int totalCurrency = items.Sum(item => item.amount);
|
||||
|
||||
if (totalCurrency < amount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int remaining = amount;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.amount > remaining)
|
||||
{
|
||||
item.UseItem(remaining);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
remaining -= item.amount;
|
||||
item.Remove();
|
||||
}
|
||||
|
||||
if (remaining <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CheckPendingInvoices()
|
||||
{
|
||||
foreach (var invoice in pendingInvoices.ToList())
|
||||
@ -927,293 +974,122 @@ namespace Oxide.Plugins
|
||||
});
|
||||
}
|
||||
|
||||
private void SendPayment(string bolt11, int satsAmount, Action<bool, string> callback)
|
||||
// UPDATED: SendPayment now deserializes using the wrapper class
|
||||
private void SendPayment(string bolt11, int satsAmount, Action<bool, string> callback)
|
||||
{
|
||||
// For outbound payments, LNbits expects only "out" and "bolt11"
|
||||
string url = $"{config.BaseUrl}/api/v1/payments";
|
||||
var requestBody = new
|
||||
{
|
||||
@out = true,
|
||||
bolt11 = bolt11
|
||||
};
|
||||
string jsonBody = JsonConvert.SerializeObject(requestBody);
|
||||
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "X-Api-Key", config.ApiKey },
|
||||
{ "Content-Type", "application/json" }
|
||||
};
|
||||
|
||||
MakeWebRequest(url, jsonBody, (code, response) =>
|
||||
{
|
||||
if (code != 200 && code != 201)
|
||||
{
|
||||
string url = $"{config.BaseUrl}/api/v1/payments";
|
||||
var requestBody = new
|
||||
{
|
||||
@out = true,
|
||||
bolt11 = bolt11,
|
||||
amount = satsAmount
|
||||
};
|
||||
string jsonBody = JsonConvert.SerializeObject(requestBody);
|
||||
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "X-Api-Key", config.ApiKey },
|
||||
{ "Content-Type", "application/json" }
|
||||
};
|
||||
|
||||
MakeWebRequest(url, jsonBody, (code, response) =>
|
||||
{
|
||||
if (code != 200 && code != 201)
|
||||
{
|
||||
PrintError($"Error processing payment: HTTP {code}");
|
||||
callback(false, null);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var paymentResponse = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
|
||||
string paymentHash = paymentResponse.ContainsKey("payment_hash") ? paymentResponse["payment_hash"].ToString() : null;
|
||||
|
||||
if (!string.IsNullOrEmpty(paymentHash))
|
||||
{
|
||||
callback(true, paymentHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError("Payment hash (rhash) is missing or invalid in the response.");
|
||||
callback(false, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PrintError($"Exception occurred while parsing payment response: {ex.Message}");
|
||||
callback(false, null);
|
||||
}
|
||||
}, RequestMethod.POST, headers);
|
||||
PrintError($"Error processing payment: HTTP {code}");
|
||||
callback(false, null);
|
||||
return;
|
||||
}
|
||||
|
||||
private void SendInvoiceToDiscord(IPlayer player, string invoice, int amountSats, string memo)
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.DiscordWebhookUrl))
|
||||
InvoiceResponse invoiceResponse = null;
|
||||
// First, attempt to deserialize using the wrapper (if present)
|
||||
try
|
||||
{
|
||||
PrintError("Discord webhook URL is not configured.");
|
||||
return;
|
||||
var wrapper = JsonConvert.DeserializeObject<InvoiceResponseWrapper>(response);
|
||||
invoiceResponse = wrapper?.Data;
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Fallback: try direct deserialization
|
||||
if (invoiceResponse == null)
|
||||
{
|
||||
invoiceResponse = JsonConvert.DeserializeObject<InvoiceResponse>(response);
|
||||
}
|
||||
|
||||
string qrCodeUrl = $"https://api.qrserver.com/v1/create-qr-code/?data={Uri.EscapeDataString(invoice)}&size=200x200";
|
||||
string paymentHash = invoiceResponse != null ? invoiceResponse.PaymentHash : null;
|
||||
|
||||
var webhookPayload = new
|
||||
if (!string.IsNullOrEmpty(paymentHash))
|
||||
{
|
||||
content = $"**{player.Name}**, please pay **{amountSats} sats** using the Lightning Network.",
|
||||
embeds = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
title = "Payment Invoice",
|
||||
description = $"{memo}\n\nPlease pay the following Lightning invoice to complete your purchase:\n\n```\n{invoice}\n```",
|
||||
image = new
|
||||
{
|
||||
url = qrCodeUrl
|
||||
},
|
||||
fields = new[]
|
||||
{
|
||||
new { name = "Amount", value = $"{amountSats} sats", inline = true },
|
||||
new { name = "Steam ID", value = GetPlayerId(player), inline = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
string jsonPayload = JsonConvert.SerializeObject(webhookPayload);
|
||||
|
||||
MakeWebRequest(config.DiscordWebhookUrl, jsonPayload, (code, response) =>
|
||||
{
|
||||
if (code != 204)
|
||||
{
|
||||
PrintError($"Failed to send invoice to Discord webhook: HTTP {code}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Puts($"Invoice sent to Discord for player {GetPlayerId(player)}.");
|
||||
}
|
||||
}, RequestMethod.POST, new Dictionary<string, string> { { "Content-Type", "application/json" } });
|
||||
}
|
||||
|
||||
private void RewardPlayer(IPlayer player, int amount)
|
||||
{
|
||||
player.Reply($"You have successfully purchased {amount} {currencyName}!");
|
||||
|
||||
var basePlayer = player.Object as BasePlayer;
|
||||
|
||||
if (basePlayer != null)
|
||||
{
|
||||
var currencyItem = ItemManager.CreateByItemID(currencyItemID, amount);
|
||||
if (currencyItem != null)
|
||||
{
|
||||
if (currencySkinID > 0)
|
||||
{
|
||||
currencyItem.skin = currencySkinID;
|
||||
}
|
||||
|
||||
basePlayer.GiveItem(currencyItem);
|
||||
Puts($"Gave {amount} {currencyName} (skinID: {currencySkinID}) to player {basePlayer.UserIDString}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to create {currencyName} item for player {basePlayer.UserIDString}.");
|
||||
}
|
||||
callback(true, paymentHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to find base player object for player {player.Id}.");
|
||||
PrintError("Payment hash (rhash) is missing or invalid in the response.");
|
||||
PrintWarning($"[SendPayment] Raw response: {response}");
|
||||
callback(false, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void GrantVip(IPlayer player)
|
||||
catch (Exception ex)
|
||||
{
|
||||
player.Reply("You have successfully purchased VIP status!");
|
||||
PrintError($"Exception occurred while parsing payment response: {ex.Message}");
|
||||
callback(false, null);
|
||||
}
|
||||
}, RequestMethod.POST, headers);
|
||||
}
|
||||
|
||||
permission.AddUserGroup(player.Id, vipPermissionGroup);
|
||||
|
||||
Puts($"Player {GetPlayerId(player)} added to VIP group '{vipPermissionGroup}'.");
|
||||
// UPDATED: CreateInvoice now deserializes using the wrapper class
|
||||
private void CreateInvoice(int amountSats, string memo, Action<InvoiceResponse> callback)
|
||||
{
|
||||
string url = $"{config.BaseUrl}/api/v1/payments";
|
||||
|
||||
var requestBody = new
|
||||
{
|
||||
@out = false,
|
||||
amount = amountSats,
|
||||
memo = memo
|
||||
};
|
||||
string jsonBody = JsonConvert.SerializeObject(requestBody);
|
||||
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "X-Api-Key", config.ApiKey },
|
||||
{ "Content-Type", "application/json" }
|
||||
};
|
||||
|
||||
MakeWebRequest(url, jsonBody, (code, response) =>
|
||||
{
|
||||
if (code != 200 && code != 201)
|
||||
{
|
||||
PrintError($"Error creating invoice: HTTP {code}");
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
private bool TryReserveCurrency(BasePlayer player, int amount)
|
||||
if (string.IsNullOrEmpty(response))
|
||||
{
|
||||
var items = GetAllInventoryItems(player).Where(IsCurrencyItem).ToList();
|
||||
int totalCurrency = items.Sum(item => item.amount);
|
||||
|
||||
if (totalCurrency < amount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int remaining = amount;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.amount > remaining)
|
||||
{
|
||||
item.UseItem(remaining);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
remaining -= item.amount;
|
||||
item.Remove();
|
||||
}
|
||||
|
||||
if (remaining <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
PrintError("Empty response received when creating invoice.");
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
private void ReturnCurrency(BasePlayer player, int amount)
|
||||
// Log the raw response for debugging purposes.
|
||||
PrintWarning($"[CreateInvoice] Raw response: {response}");
|
||||
|
||||
try
|
||||
{
|
||||
var returnedCurrency = ItemManager.CreateByItemID(currencyItemID, amount);
|
||||
if (returnedCurrency != null)
|
||||
{
|
||||
if (currencySkinID > 0)
|
||||
{
|
||||
returnedCurrency.skin = currencySkinID;
|
||||
}
|
||||
returnedCurrency.MoveToContainer(player.inventory.containerMain);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to create {currencyName} item to return to player {player.UserIDString}.");
|
||||
}
|
||||
var invoiceResponse = JsonConvert.DeserializeObject<InvoiceResponse>(response);
|
||||
callback(invoiceResponse != null && !string.IsNullOrEmpty(invoiceResponse.PaymentHash) ? invoiceResponse : null);
|
||||
}
|
||||
|
||||
private bool IsCurrencyItem(Item item)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return item.info.itemid == currencyItemID && (currencySkinID == 0 || item.skin == currencySkinID);
|
||||
}
|
||||
|
||||
private void LogSellTransaction(SellInvoiceLogEntry logEntry)
|
||||
{
|
||||
var logs = LoadSellLogData();
|
||||
logs.Add(logEntry);
|
||||
SaveSellLogData(logs);
|
||||
Puts($"[Orangemart] Logged sell transaction: {JsonConvert.SerializeObject(logEntry)}");
|
||||
}
|
||||
|
||||
private List<SellInvoiceLogEntry> LoadSellLogData()
|
||||
{
|
||||
var path = Path.Combine(Interface.Oxide.DataDirectory, SellLogFile);
|
||||
return File.Exists(path)
|
||||
? JsonConvert.DeserializeObject<List<SellInvoiceLogEntry>>(File.ReadAllText(path))
|
||||
: new List<SellInvoiceLogEntry>();
|
||||
}
|
||||
|
||||
private void SaveSellLogData(List<SellInvoiceLogEntry> data)
|
||||
{
|
||||
var path = Path.Combine(Interface.Oxide.DataDirectory, SellLogFile);
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
}
|
||||
|
||||
private void LogBuyInvoice(BuyInvoiceLogEntry logEntry)
|
||||
{
|
||||
var logPath = Path.Combine(Interface.Oxide.DataDirectory, BuyInvoiceLogFile);
|
||||
var directory = Path.GetDirectoryName(logPath);
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
List<BuyInvoiceLogEntry> invoiceLogs = File.Exists(logPath)
|
||||
? JsonConvert.DeserializeObject<List<BuyInvoiceLogEntry>>(File.ReadAllText(logPath)) ?? new List<BuyInvoiceLogEntry>()
|
||||
: new List<BuyInvoiceLogEntry>();
|
||||
|
||||
invoiceLogs.Add(logEntry);
|
||||
File.WriteAllText(logPath, JsonConvert.SerializeObject(invoiceLogs, Formatting.Indented));
|
||||
Puts($"[Orangemart] Logged buy invoice: {JsonConvert.SerializeObject(logEntry)}");
|
||||
}
|
||||
|
||||
private BuyInvoiceLogEntry CreateBuyInvoiceLogEntry(IPlayer player, string invoiceID, bool isPaid, int amount, PurchaseType type, int retryCount)
|
||||
{
|
||||
return new BuyInvoiceLogEntry
|
||||
{
|
||||
SteamID = GetPlayerId(player),
|
||||
InvoiceID = invoiceID,
|
||||
IsPaid = isPaid,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Amount = type == PurchaseType.SendBitcoin ? amount : amount * pricePerCurrencyUnit,
|
||||
CurrencyGiven = isPaid && type == PurchaseType.Currency,
|
||||
VipGranted = isPaid && type == PurchaseType.Vip,
|
||||
RetryCount = retryCount
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateInvoice(int amountSats, string memo, Action<InvoiceResponse> callback)
|
||||
{
|
||||
string url = $"{config.BaseUrl}/api/v1/payments";
|
||||
|
||||
var requestBody = new
|
||||
{
|
||||
@out = false,
|
||||
amount = amountSats,
|
||||
memo = memo
|
||||
};
|
||||
string jsonBody = JsonConvert.SerializeObject(requestBody);
|
||||
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "X-Api-Key", config.ApiKey },
|
||||
{ "Content-Type", "application/json" }
|
||||
};
|
||||
|
||||
MakeWebRequest(url, jsonBody, (code, response) =>
|
||||
{
|
||||
if (code != 200 && code != 201)
|
||||
{
|
||||
PrintError($"Error creating invoice: HTTP {code}");
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var invoiceResponse = JsonConvert.DeserializeObject<InvoiceResponse>(response);
|
||||
callback(invoiceResponse != null && !string.IsNullOrEmpty(invoiceResponse.PaymentHash) ? invoiceResponse : null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PrintError($"Failed to deserialize invoice response: {ex.Message}");
|
||||
callback(null);
|
||||
}
|
||||
}, RequestMethod.POST, headers);
|
||||
PrintError($"Failed to deserialize invoice response: {ex.Message}");
|
||||
callback(null);
|
||||
}
|
||||
}, RequestMethod.POST, headers);
|
||||
}
|
||||
|
||||
private string GetPlayerId(IPlayer player)
|
||||
{
|
||||
@ -1356,10 +1232,170 @@ namespace Oxide.Plugins
|
||||
|
||||
private string ExtractLightningAddress(string memo)
|
||||
{
|
||||
// Extract the Lightning Address from the memo
|
||||
// Expected format: "Sending {amount} {currency} to {lightning_address}"
|
||||
var parts = memo.Split(" to ");
|
||||
return parts.Length == 2 ? parts[1] : "unknown@unknown.com";
|
||||
}
|
||||
|
||||
// NEW: RewardPlayer method to grant currency items to the player
|
||||
private void RewardPlayer(IPlayer player, int amount)
|
||||
{
|
||||
player.Reply($"You have successfully purchased {amount} {currencyName}!");
|
||||
|
||||
var basePlayer = player.Object as BasePlayer;
|
||||
if (basePlayer != null)
|
||||
{
|
||||
var currencyItem = ItemManager.CreateByItemID(currencyItemID, amount);
|
||||
if (currencyItem != null)
|
||||
{
|
||||
if (currencySkinID > 0)
|
||||
{
|
||||
currencyItem.skin = currencySkinID;
|
||||
}
|
||||
basePlayer.GiveItem(currencyItem);
|
||||
Puts($"Gave {amount} {currencyName} (skinID: {currencySkinID}) to player {basePlayer.UserIDString}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to create {currencyName} item for player {basePlayer.UserIDString}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to find base player object for player {player.Id}.");
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: GrantVip method to add the VIP permission group to the player
|
||||
private void GrantVip(IPlayer player)
|
||||
{
|
||||
player.Reply("You have successfully purchased VIP status!");
|
||||
permission.AddUserGroup(player.Id, vipPermissionGroup);
|
||||
Puts($"Player {GetPlayerId(player)} added to VIP group '{vipPermissionGroup}'.");
|
||||
}
|
||||
|
||||
// NEW: ReturnCurrency method to refund currency items to the player
|
||||
private void ReturnCurrency(BasePlayer player, int amount)
|
||||
{
|
||||
var returnedCurrency = ItemManager.CreateByItemID(currencyItemID, amount);
|
||||
if (returnedCurrency != null)
|
||||
{
|
||||
if (currencySkinID > 0)
|
||||
{
|
||||
returnedCurrency.skin = currencySkinID;
|
||||
}
|
||||
returnedCurrency.MoveToContainer(player.inventory.containerMain);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError($"Failed to create {currencyName} item to return to player {player.UserIDString}.");
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: LogSellTransaction helper
|
||||
private void LogSellTransaction(SellInvoiceLogEntry logEntry)
|
||||
{
|
||||
var logs = LoadSellLogData();
|
||||
logs.Add(logEntry);
|
||||
SaveSellLogData(logs);
|
||||
Puts($"[Orangemart] Logged sell transaction: {JsonConvert.SerializeObject(logEntry)}");
|
||||
}
|
||||
|
||||
private List<SellInvoiceLogEntry> LoadSellLogData()
|
||||
{
|
||||
var path = Path.Combine(Interface.Oxide.DataDirectory, SellLogFile);
|
||||
return File.Exists(path)
|
||||
? JsonConvert.DeserializeObject<List<SellInvoiceLogEntry>>(File.ReadAllText(path))
|
||||
: new List<SellInvoiceLogEntry>();
|
||||
}
|
||||
|
||||
private void SaveSellLogData(List<SellInvoiceLogEntry> data)
|
||||
{
|
||||
var path = Path.Combine(Interface.Oxide.DataDirectory, SellLogFile);
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
}
|
||||
|
||||
// NEW: LogBuyInvoice helper
|
||||
private void LogBuyInvoice(BuyInvoiceLogEntry logEntry)
|
||||
{
|
||||
var logPath = Path.Combine(Interface.Oxide.DataDirectory, BuyInvoiceLogFile);
|
||||
var directory = Path.GetDirectoryName(logPath);
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
List<BuyInvoiceLogEntry> invoiceLogs = File.Exists(logPath)
|
||||
? JsonConvert.DeserializeObject<List<BuyInvoiceLogEntry>>(File.ReadAllText(logPath)) ?? new List<BuyInvoiceLogEntry>()
|
||||
: new List<BuyInvoiceLogEntry>();
|
||||
|
||||
invoiceLogs.Add(logEntry);
|
||||
File.WriteAllText(logPath, JsonConvert.SerializeObject(invoiceLogs, Formatting.Indented));
|
||||
Puts($"[Orangemart] Logged buy invoice: {JsonConvert.SerializeObject(logEntry)}");
|
||||
}
|
||||
|
||||
private BuyInvoiceLogEntry CreateBuyInvoiceLogEntry(IPlayer player, string invoiceID, bool isPaid, int amount, PurchaseType type, int retryCount)
|
||||
{
|
||||
return new BuyInvoiceLogEntry
|
||||
{
|
||||
SteamID = GetPlayerId(player),
|
||||
InvoiceID = invoiceID,
|
||||
IsPaid = isPaid,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Amount = type == PurchaseType.SendBitcoin ? amount : amount * pricePerCurrencyUnit,
|
||||
CurrencyGiven = isPaid && type == PurchaseType.Currency,
|
||||
VipGranted = isPaid && type == PurchaseType.Vip,
|
||||
RetryCount = retryCount
|
||||
};
|
||||
}
|
||||
|
||||
private void SendInvoiceToDiscord(IPlayer player, string invoice, int amountSats, string memo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.DiscordWebhookUrl))
|
||||
{
|
||||
PrintError("Discord webhook URL is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
string qrCodeUrl = $"https://api.qrserver.com/v1/create-qr-code/?data={Uri.EscapeDataString(invoice)}&size=200x200";
|
||||
|
||||
var webhookPayload = new
|
||||
{
|
||||
content = $"**{player.Name}**, please pay **{amountSats} sats** using the Lightning Network.",
|
||||
embeds = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
title = "Payment Invoice",
|
||||
description = $"{memo}\n\nPlease pay the following Lightning invoice to complete your purchase:\n\n```\n{invoice}\n```",
|
||||
image = new
|
||||
{
|
||||
url = qrCodeUrl
|
||||
},
|
||||
fields = new[]
|
||||
{
|
||||
new { name = "Amount", value = $"{amountSats} sats", inline = true },
|
||||
new { name = "Steam ID", value = GetPlayerId(player), inline = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
string jsonPayload = JsonConvert.SerializeObject(webhookPayload);
|
||||
|
||||
MakeWebRequest(config.DiscordWebhookUrl, jsonPayload, (code, response) =>
|
||||
{
|
||||
if (code != 204)
|
||||
{
|
||||
PrintError($"Failed to send invoice to Discord webhook: HTTP {code}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Puts($"Invoice sent to Discord for player {GetPlayerId(player)}.");
|
||||
}
|
||||
}, RequestMethod.POST, new Dictionary<string, string> { { "Content-Type", "application/json" } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user