🔧 Architecture Universelle

Toutes les plateformes se connectent au backend Joomla de ZoneCoin via une passerelle API REST unique.

Platform SDKs
🌐OpenSimC# .NET
🔵Second LifeLSL
UnityC#
🔷UnrealC++
🔹GodotGDScript
🟫MinecraftJava
🔴RobloxLuau
🟣VRChatUdonSharp
🏙️DecentralandTypeScript
🏖️The SandboxTypeScript
API Gateway
REST API (HTTPS + HMAC-SHA256) /api/index.php/v1/zonecoin/*
Core Engine
Joomla + ZoneCoin Component Wallets · Ledger · Exchange · NFTs · Staking · Governance

⚡ Démarrage Rapide

Quatre étapes pour intégrer ZoneCoin dans n'importe quelle plateforme.

1
Enregistrer le Grid
Inscrivez-vous et enregistrez votre grid/serveur pour recevoir les identifiants API.
2
Obtenir les Clés API
Générez votre clé API, le secret et l'ID marchand depuis le tableau de bord.
3
Installer le SDK
Téléchargez le SDK ou le module DLL pour votre plateforme cible.
4
Mise en Production
Testez en sandbox, puis passez en production — vous êtes prêt !

🌐 OpenSimulator

ZoneCoin fournit un module natif C# .NET qui s'integre directement dans n'importe quelle grille OpenSimulator, permettant la gestion de portefeuilles en temps reel, les transferts entre avatars, les achats de terrains et les transactions de marketplace — le tout verifie via la blockchain PoA ZoneCoin.

💰
Requete de SoldeSolde du portefeuille en temps reel via REST API avec support multi-devises.
🔄
Transfert P2PTransferts instantanes entre avatars avec verification cryptographique.
🏠
Paiement TerrainAchat et location de terrains automatises avec support escrow.
🛒
MarketplaceAchat d'objets in-world avec reglement marchand et recus.

📥 Telecharger le Module OpenSim (DLL)

🔵 Second Life

Integrez ZoneCoin dans Second Life avec des scripts LSL. Creez des terminaux de paiement, des verificateurs de solde et des vendeurs automatises connectes a l'API REST ZoneCoin via des requetes HTTP.

Terminal de Paiement

LSL// ZoneCoin Payment Terminal — Second Life // Drop this script into any prim to create a payment terminal string API_URL = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"; string API_KEY = "YOUR_API_KEY"; string MERCHANT = "your-merchant-id"; key httpReq; default { state_entry() { llSetText("ZoneCoin Payment Terminal\nTouch to pay", <0.4, 0.9, 1.0>, 1.0); llSetTimerEvent(300.0); // Heartbeat every 5 min } touch_start(integer n) { key buyer = llDetectedKey(0); string name = llDetectedName(0); llTextBox(buyer, "Enter amount in ZC:", 1); } listen(integer ch, string name, key id, string msg) { float amount = (float)msg; if (amount <= 0) { llInstantMessage(id, "Invalid amount."); return; } string body = llList2Json(JSON_OBJECT, [ "action", "pay", "amount", (string)amount, "ref", "SL-" + (string)llGetUnixTime(), "buyer", (string)id, "merchant", MERCHANT ]); httpReq = llHTTPRequest(API_URL + "/pay", [ HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/json", HTTP_CUSTOM_HEADER, "Authorization", "Bearer " + API_KEY, HTTP_CUSTOM_HEADER, "X-ZoneCoin-Timestamp", (string)llGetUnixTime() ], body); } http_response(key req, integer status, list meta, string body) { if (req != httpReq) return; if (status == 200) { string txId = llJsonGetValue(body, ["txId"]); llSay(0, "✅ Payment confirmed! TX: " + txId); } else { llSay(0, "❌ Payment failed: " + body); } } timer() { llHTTPRequest(API_URL + "/heartbeat", [ HTTP_METHOD, "GET", HTTP_CUSTOM_HEADER, "Authorization", "Bearer " + API_KEY ], ""); } }

Verificateur de Solde

LSL// ZoneCoin Balance Checker — Second Life // Touch to check your ZoneCoin wallet balance string API_URL = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"; string API_KEY = "YOUR_API_KEY"; key httpReq; key requester; default { state_entry() { llSetText("ZoneCoin Balance\nTouch to check", <0.2, 0.8, 0.6>, 1.0); } touch_start(integer n) { requester = llDetectedKey(0); httpReq = llHTTPRequest(API_URL + "/balance/" + (string)requester, [ HTTP_METHOD, "GET", HTTP_CUSTOM_HEADER, "Authorization", "Bearer " + API_KEY ], ""); } http_response(key req, integer status, list meta, string body) { if (req != httpReq) return; if (status == 200) { string bal = llJsonGetValue(body, ["balance"]); string cur = llJsonGetValue(body, ["currency"]); llInstantMessage(requester, "💰 Your ZoneCoin balance: " + bal + " " + cur); } else { llInstantMessage(requester, "❌ Could not retrieve balance."); } } }

⬛ Unity

Le SDK Unity ZoneCoin fournit un singleton base sur MonoBehaviour avec support complet async/coroutine. Gerez les paiements, les requetes de solde et les transferts peer avec signature automatique HMAC-SHA256.

ZoneCoinSDK.cs

C#using System; using System.Collections; using System.Security.Cryptography; using System.Text; using UnityEngine; using UnityEngine.Networking; namespace ZoneCoin.SDK { /// <summary> /// ZoneCoin SDK for Unity — handles payments, balance queries /// and webhook verification via the ZoneCoin REST API. /// </summary> public class ZoneCoinSDK : MonoBehaviour { [Header("Configuration")] [SerializeField] private string apiUrl = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"; [SerializeField] private string apiKey = ""; [SerializeField] private string secret = ""; [SerializeField] private string merchant = ""; public static ZoneCoinSDK Instance { get; private set; } private void Awake() { if (Instance != null) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); } // ── Balance ─────────────────────────────────────────── public void GetBalance(string wallet, Action<decimal> onSuccess, Action<string> onError = null) { StartCoroutine(GetRequest($"{apiUrl}/balance/{wallet}", json => { var balance = decimal.Parse(JsonUtility.FromJson<BalanceResponse>(json).balance); onSuccess?.Invoke(balance); }, onError)); } // ── Pay ─────────────────────────────────────────────── public void Pay(decimal amount, string reference, bool isFiat, Action<PayResult> onSuccess, Action<string> onError = null) { var body = JsonUtility.ToJson(new PayRequest { amount = amount.ToString("F2"), ref_id = reference, merchant = merchant, isFiat = isFiat }); StartCoroutine(PostRequest($"{apiUrl}/pay", body, json => { onSuccess?.Invoke(JsonUtility.FromJson<PayResult>(json)); }, onError)); } // ── Transfer ────────────────────────────────────────── public void Transfer(string toWallet, decimal amount, Action<string> onSuccess, Action<string> onError = null) { var body = JsonUtility.ToJson(new TransferRequest { to = toWallet, amount = amount.ToString("F2") }); StartCoroutine(PostRequest($"{apiUrl}/transfer", body, json => { onSuccess?.Invoke(JsonUtility.FromJson<TxResponse>(json).txId); }, onError)); } // ── HMAC Signing ────────────────────────────────────── private string Sign(string timestamp, string nonce, string body) { var payload = timestamp + nonce + body; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); return BitConverter.ToString(hash).Replace("-", "").ToLower(); } // ── HTTP Helpers ────────────────────────────────────── private IEnumerator GetRequest(string url, Action<string> onOk, Action<string> onErr) { using var req = UnityWebRequest.Get(url); AddHeaders(req, ""); yield return req.SendWebRequest(); if (req.result == UnityWebRequest.Result.Success) onOk(req.downloadHandler.text); else onErr?.Invoke(req.error); } private IEnumerator PostRequest(string url, string json, Action<string> onOk, Action<string> onErr) { using var req = new UnityWebRequest(url, "POST"); req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)); req.downloadHandler = new DownloadHandlerBuffer(); req.SetRequestHeader("Content-Type", "application/json"); AddHeaders(req, json); yield return req.SendWebRequest(); if (req.result == UnityWebRequest.Result.Success) onOk(req.downloadHandler.text); else onErr?.Invoke(req.error); } private void AddHeaders(UnityWebRequest req, string body) { var ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); var nonce = Guid.NewGuid().ToString("N").Substring(0, 12); req.SetRequestHeader("Authorization", "Bearer " + apiKey); req.SetRequestHeader("X-ZoneCoin-Timestamp", ts); req.SetRequestHeader("X-ZoneCoin-Nonce", nonce); req.SetRequestHeader("X-ZoneCoin-Signature", Sign(ts, nonce, body)); } // ── DTOs ────────────────────────────────────────────── [Serializable] public class BalanceResponse { public string balance; public string currency; } [Serializable] public class PayRequest { public string amount, ref_id, merchant; public bool isFiat; } [Serializable] public class PayResult { public string txId; public string amount; } [Serializable] public class TransferRequest { public string to, amount; } [Serializable] public class TxResponse { public string txId; } } }

Exemple d'Utilisation

C#// Example: In-game shop purchase using ZoneCoin.SDK; public class ShopUI : MonoBehaviour { public void OnBuyItem(string itemId, decimal price) { ZoneCoinSDK.Instance.Pay(price, $"GAME-{itemId}", isFiat: false, onSuccess: result => { Debug.Log($"Payment OK! TX: {result.txId}"); InventoryManager.Instance.AddItem(itemId); UIManager.Instance.ShowToast("Purchase complete! ⚡"); }, onError: err => { Debug.LogError($"Payment failed: {err}"); UIManager.Instance.ShowToast("Payment failed. Try again."); } ); } }

🔷 Unreal Engine

L'integration Unreal Engine utilise un GameInstanceSubsystem exposant des fonctions BlueprintCallable pour les paiements, les requetes de solde et les transferts. Toutes les requetes sont signees HMAC et traitees de maniere asynchrone.

ZoneCoinSubsystem.h

C++#pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "Http.h" #include "ZoneCoinSubsystem.generated.h" DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnZCPayResult, bool, bSuccess, const FString&, TxId); DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnZCBalance, bool, bSuccess, float, Balance); UCLASS() class YOURPROJECT_API UZoneCoinSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ZoneCoin") FString ApiUrl = TEXT("https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ZoneCoin") FString ApiKey; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ZoneCoin") FString Secret; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ZoneCoin") FString MerchantId; UFUNCTION(BlueprintCallable, Category = "ZoneCoin") void Pay(float Amount, const FString& Reference, bool bIsFiat, FOnZCPayResult OnResult); UFUNCTION(BlueprintCallable, Category = "ZoneCoin") void GetBalance(const FString& Wallet, FOnZCBalance OnResult); UFUNCTION(BlueprintCallable, Category = "ZoneCoin") void Transfer(const FString& ToWallet, float Amount, FOnZCPayResult OnResult); private: TSharedRef<IHttpRequest> CreateRequest(const FString& Verb, const FString& Endpoint, const FString& Body = TEXT("")); FString SignRequest(const FString& Timestamp, const FString& Nonce, const FString& Body); };

ZoneCoinSubsystem.cpp

C++#include "ZoneCoinSubsystem.h" #include "Serialization/JsonSerializer.h" #include "Misc/SecureHash.h" void UZoneCoinSubsystem::Pay(float Amount, const FString& Reference, bool bIsFiat, FOnZCPayResult OnResult) { TSharedPtr<FJsonObject> Json = MakeShareable(new FJsonObject); Json->SetNumberField(TEXT("amount"), Amount); Json->SetStringField(TEXT("ref"), Reference); Json->SetStringField(TEXT("merchant"), MerchantId); Json->SetBoolField(TEXT("isFiat"), bIsFiat); FString Body; auto Writer = TJsonWriterFactory<>::Create(&Body); FJsonSerializer::Serialize(Json.ToSharedRef(), Writer); auto Request = CreateRequest(TEXT("POST"), TEXT("/pay"), Body); Request->OnProcessRequestComplete().BindLambda( [OnResult](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bOk) { if (bOk && Res->GetResponseCode() == 200) { TSharedPtr<FJsonObject> R; auto Reader = TJsonReaderFactory<>::Create(Res->GetContentAsString()); FJsonSerializer::Deserialize(Reader, R); OnResult.ExecuteIfBound(true, R->GetStringField(TEXT("txId"))); } else { OnResult.ExecuteIfBound(false, TEXT("")); } }); Request->ProcessRequest(); } void UZoneCoinSubsystem::GetBalance(const FString& Wallet, FOnZCBalance OnResult) { auto Request = CreateRequest(TEXT("GET"), FString::Printf(TEXT("/balance/%s"), *Wallet)); Request->OnProcessRequestComplete().BindLambda( [OnResult](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bOk) { if (bOk && Res->GetResponseCode() == 200) { TSharedPtr<FJsonObject> R; auto Reader = TJsonReaderFactory<>::Create(Res->GetContentAsString()); FJsonSerializer::Deserialize(Reader, R); OnResult.ExecuteIfBound(true, R->GetNumberField(TEXT("balance"))); } else { OnResult.ExecuteIfBound(false, 0.f); } }); Request->ProcessRequest(); }

🔹 Godot

Le SDK ZoneCoin pour Godot 4 est un singleton autoload GDScript utilisant les signaux pour la communication asynchrone. Supporte les paiements, les requetes de solde et les transferts avec signature HMAC-SHA256 integree via HMACContext.

zonecoin_sdk.gd

GDScriptextends Node class_name ZoneCoinSDK ## ZoneCoin SDK for Godot 4 — REST API integration ## Add this as an autoload singleton: Project → Settings → Autoload @export var api_url : String = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin" @export var api_key : String = "" @export var secret : String = "" @export var merchant : String = "" signal payment_completed(tx_id: String, amount: float) signal payment_failed(error: String) signal balance_received(balance: float, currency: String) var _http : HTTPRequest func _ready(): _http = HTTPRequest.new() add_child(_http) # ── Pay ──────────────────────────────────────────────────── func pay(amount: float, reference: String, is_fiat: bool = false) -> void: var body = JSON.stringify({ "amount": amount, "ref": reference, "merchant": merchant, "isFiat": is_fiat }) var headers = _build_headers(body) _http.request_completed.connect(_on_pay_response, CONNECT_ONE_SHOT) _http.request(api_url + "/pay", headers, HTTPClient.METHOD_POST, body) func _on_pay_response(result, code, headers, body): var json = JSON.parse_string(body.get_string_from_utf8()) if code == 200: payment_completed.emit(json.txId, float(json.amount)) else: payment_failed.emit(json.get("error", "Unknown error")) # ── Balance ──────────────────────────────────────────────── func get_balance(wallet: String) -> void: var headers = _build_headers("") _http.request_completed.connect(_on_balance_response, CONNECT_ONE_SHOT) _http.request(api_url + "/balance/" + wallet, headers, HTTPClient.METHOD_GET) func _on_balance_response(result, code, headers, body): var json = JSON.parse_string(body.get_string_from_utf8()) if code == 200: balance_received.emit(float(json.balance), json.currency) # ── Transfer ─────────────────────────────────────────────── func transfer(to_wallet: String, amount: float) -> void: var body = JSON.stringify({"to": to_wallet, "amount": amount}) var headers = _build_headers(body) _http.request(api_url + "/transfer", headers, HTTPClient.METHOD_POST, body) # ── HMAC Signing ─────────────────────────────────────────── func _build_headers(body: String) -> PackedStringArray: var ts = str(int(Time.get_unix_time_from_system())) var nonce = _rand_hex(12) var sig = _hmac_sha256(ts + nonce + body) return PackedStringArray([ "Content-Type: application/json", "Authorization: Bearer " + api_key, "X-ZoneCoin-Timestamp: " + ts, "X-ZoneCoin-Nonce: " + nonce, "X-ZoneCoin-Signature: " + sig ]) func _hmac_sha256(message: String) -> String: var ctx = HMACContext.new() ctx.start(HashingContext.HASH_SHA256, secret.to_utf8_buffer()) ctx.update(message.to_utf8_buffer()) return ctx.finish().hex_encode() func _rand_hex(length: int) -> String: var bytes = Crypto.new().generate_random_bytes(length) return bytes.hex_encode().substr(0, length)

🟫 Minecraft

Le plugin ZoneCoin pour Minecraft s'integre aux serveurs Spigot et Paper. Les joueurs utilisent /zcbalance et /zcpay pour gerer leurs portefeuilles. Supporte les appels HTTP asynchrones et l'authentification HMAC.

plugin.yml

YAMLname: ZoneCoinPlugin version: 1.0.0 main: com.zonenations.zonecoin.ZoneCoinPlugin api-version: '1.20' description: ZoneCoin virtual currency for Minecraft servers authors: [ZoneNations] commands: zcbalance: description: Check your ZoneCoin balance usage: /zcbalance permission: zonecoin.balance zcpay: description: Send ZoneCoin to a player usage: /zcpay <player> <amount> permission: zonecoin.pay zcshop: description: Open the ZoneCoin shop usage: /zcshop permission: zonecoin.shop permissions: zonecoin.balance: default: true zonecoin.pay: default: true zonecoin.shop: default: true zonecoin.admin: default: op

ZoneCoinPlugin.java

Javapackage com.zonenations.zonecoin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.command.*; import org.bukkit.entity.Player; import com.google.gson.*; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.http.*; import java.net.URI; import java.time.Instant; import java.util.UUID; public class ZoneCoinPlugin extends JavaPlugin { private String apiUrl, apiKey, secret, merchant; private final HttpClient http = HttpClient.newHttpClient(); private final Gson gson = new Gson(); @Override public void onEnable() { saveDefaultConfig(); apiUrl = getConfig().getString("api-url", "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"); apiKey = getConfig().getString("api-key", ""); secret = getConfig().getString("secret", ""); merchant = getConfig().getString("merchant", ""); getCommand("zcbalance").setExecutor(this::onBalance); getCommand("zcpay").setExecutor(this::onPay); getLogger().info("ZoneCoin plugin enabled ⚡"); } private boolean onBalance(CommandSender s, Command c, String l, String[] a) { if (!(s instanceof Player p)) { s.sendMessage("§cPlayers only."); return true; } getServer().getScheduler().runTaskAsynchronously(this, () -> { try { var res = apiGet("/balance/" + p.getUniqueId()); var json = JsonParser.parseString(res).getAsJsonObject(); var bal = json.get("balance").getAsString(); p.sendMessage("§6§l💰 ZoneCoin Balance: §f" + bal + " ZC"); } catch (Exception e) { p.sendMessage("§c❌ Could not retrieve balance."); getLogger().warning("Balance error: " + e.getMessage()); } }); return true; } private boolean onPay(CommandSender s, Command c, String l, String[] a) { if (!(s instanceof Player p)) return false; if (a.length < 2) { p.sendMessage("§cUsage: /zcpay <player> <amount>"); return true; } Player target = getServer().getPlayer(a[0]); if (target == null) { p.sendMessage("§cPlayer not found."); return true; } double amount; try { amount = Double.parseDouble(a[1]); } catch (Exception e) { p.sendMessage("§cInvalid amount."); return true; } getServer().getScheduler().runTaskAsynchronously(this, () -> { try { var body = gson.toJson(new PayReq(amount, "MC-" + UUID.randomUUID().toString().substring(0, 8), p.getUniqueId().toString(), target.getUniqueId().toString())); var res = apiPost("/transfer", body); var txId = JsonParser.parseString(res).getAsJsonObject().get("txId").getAsString(); p.sendMessage("§a§l✅ Sent " + amount + " ZC to " + target.getName() + " (TX: " + txId + ")"); target.sendMessage("§a§l💰 Received " + amount + " ZC from " + p.getName()); } catch (Exception e) { p.sendMessage("§c❌ Transfer failed: " + e.getMessage()); } }); return true; } // ── HTTP Helpers with HMAC ───────────────────────────────── private String apiGet(String endpoint) throws Exception { var ts = String.valueOf(Instant.now().getEpochSecond()); var nonce = UUID.randomUUID().toString().substring(0, 12); var sig = hmacSha256(ts + nonce); var req = HttpRequest.newBuilder(URI.create(apiUrl + endpoint)) .header("Authorization", "Bearer " + apiKey) .header("X-ZoneCoin-Timestamp", ts) .header("X-ZoneCoin-Nonce", nonce) .header("X-ZoneCoin-Signature", sig) .GET().build(); return http.send(req, HttpResponse.BodyHandlers.ofString()).body(); } private String apiPost(String endpoint, String body) throws Exception { var ts = String.valueOf(Instant.now().getEpochSecond()); var nonce = UUID.randomUUID().toString().substring(0, 12); var sig = hmacSha256(ts + nonce + body); var req = HttpRequest.newBuilder(URI.create(apiUrl + endpoint)) .header("Authorization", "Bearer " + apiKey) .header("Content-Type", "application/json") .header("X-ZoneCoin-Timestamp", ts) .header("X-ZoneCoin-Nonce", nonce) .header("X-ZoneCoin-Signature", sig) .POST(HttpRequest.BodyPublishers.ofString(body)).build(); return http.send(req, HttpResponse.BodyHandlers.ofString()).body(); } private String hmacSha256(String data) throws Exception { var mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256")); var hex = new StringBuilder(); for (byte b : mac.doFinal(data.getBytes())) hex.append(String.format("%02x", b)); return hex.toString(); } record PayReq(double amount, String ref, String from, String to) {} }

🔴 Roblox

Le SDK ZoneCoin pour Roblox est un ModuleScript cote serveur utilisant HttpService pour la communication API. Supporte les requetes de solde, les paiements et les transferts peer avec gestion d'erreurs pcall.

ZoneCoinSDK (ModuleScript)

Luau-- ZoneCoin SDK for Roblox — Server-side ModuleScript -- Place in ServerScriptService/ZoneCoinSDK local HttpService = game:GetService("HttpService") local ZoneCoin = {} ZoneCoin.__index = ZoneCoin local API_URL = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin" local API_KEY = "" -- Set in config local SECRET = "" local MERCHANT = "" function ZoneCoin.Init(config) API_URL = config.apiUrl or API_URL API_KEY = config.apiKey or API_KEY SECRET = config.secret or SECRET MERCHANT = config.merchant or MERCHANT end function ZoneCoin.GetBalance(playerId) local ok, res = pcall(function() return HttpService:RequestAsync({ Url = API_URL .. "/balance/" .. tostring(playerId), Method = "GET", Headers = ZoneCoin._Headers("") }) end) if ok and res.StatusCode == 200 then local data = HttpService:JSONDecode(res.Body) return tonumber(data.balance), data.currency end return nil, "Request failed" end function ZoneCoin.Pay(amount, reference, isFiat) local body = HttpService:JSONEncode({ amount = amount, ref = reference, merchant = MERCHANT, isFiat = isFiat or false }) local ok, res = pcall(function() return HttpService:RequestAsync({ Url = API_URL .. "/pay", Method = "POST", Headers = ZoneCoin._Headers(body), Body = body }) end) if ok and res.StatusCode == 200 then local data = HttpService:JSONDecode(res.Body) return data.txId end return nil end function ZoneCoin.Transfer(toPlayer, amount) local body = HttpService:JSONEncode({to = tostring(toPlayer), amount = amount}) local ok, res = pcall(function() return HttpService:RequestAsync({ Url = API_URL .. "/transfer", Method = "POST", Headers = ZoneCoin._Headers(body), Body = body }) end) if ok and res.StatusCode == 200 then return HttpService:JSONDecode(res.Body).txId end return nil end function ZoneCoin._Headers(body) local ts = tostring(os.time()) local nonce = HttpService:GenerateGUID(false):sub(1, 12) return { ["Content-Type"] = "application/json", ["Authorization"] = "Bearer " .. API_KEY, ["X-ZoneCoin-Timestamp"] = ts, ["X-ZoneCoin-Nonce"] = nonce, } end return ZoneCoin

Script Serveur — Gestionnaire de Boutique

Luau-- Server Script — Shop handler local ZoneCoin = require(game.ServerScriptService.ZoneCoinSDK) local ReplicatedStorage = game:GetService("ReplicatedStorage") ZoneCoin.Init({ apiUrl = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin", apiKey = "YOUR_API_KEY", secret = "YOUR_SECRET", merchant = "your-merchant" }) -- Remote event for purchase requests local BuyEvent = ReplicatedStorage:WaitForChild("BuyItem") BuyEvent.OnServerEvent:Connect(function(player, itemId, price) local txId = ZoneCoin.Pay(price, "RBLX-" .. itemId .. "-" .. player.UserId, false) if txId then -- Grant item to player player:FindFirstChild("Inventory"):FindFirstChild(itemId).Value = true BuyEvent:FireClient(player, true, txId) else BuyEvent:FireClient(player, false, "Payment failed") end end)

🟣 VRChat

L'integration VRChat utilise UdonSharp pour creer des terminaux de paiement interactifs dans les mondes VRChat. Utilise VRCStringDownloader pour la communication API et la synchronisation manuelle pour l'etat multi-utilisateur.

ZoneCoinTerminal.cs (UdonSharp)

C# (UdonSharp)using UdonSharp; using UnityEngine; using VRC.SDKBase; using VRC.SDK3.StringLoading; using VRC.Udon.Common.Interfaces; /// <summary> /// ZoneCoin Payment Terminal for VRChat worlds. /// Attach to a UI Canvas with display and button references. /// </summary> [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class ZoneCoinTerminal : UdonSharpBehaviour { [Header("ZoneCoin Config")] public string apiUrl = "https://zonecoin.zonenations.com/api/index.php/v1/zonecoin"; public string apiKey = ""; public string merchant = ""; [Header("UI References")] public UnityEngine.UI.Text balanceText; public UnityEngine.UI.Text statusText; [UdonSynced] private string lastTxId = ""; public override void Interact() { // Check balance on interact VRCStringDownloader.LoadUrl( new VRCUrl($"{apiUrl}/balance/{Networking.LocalPlayer.displayName}"), (IUdonEventReceiver)this ); statusText.text = "⏳ Checking balance..."; } public override void OnStringLoadSuccess(IVRCStringDownload result) { // Parse simple JSON response string body = result.Result; // Basic JSON extraction (VRChat limitation: no full JSON parser) string balance = ExtractJsonValue(body, "balance"); string currency = ExtractJsonValue(body, "currency"); balanceText.text = $"💰 {balance} {currency}"; statusText.text = "✅ Balance loaded"; } public override void OnStringLoadError(IVRCStringDownload result) { statusText.text = "❌ Connection error"; } private string ExtractJsonValue(string json, string key) { int start = json.IndexOf($"\"{key}\":\"") + key.Length + 4; int end = json.IndexOf("\"", start); return (start > 0 && end > start) ? json.Substring(start, end - start) : "?"; } }

🏙️ Decentraland

Integrez ZoneCoin dans les scenes Decentraland avec SDK 7. Les terminaux de paiement sont crees comme entites interactives utilisant PointerEvents et signedFetch pour les appels API authentifies.

ZoneCoinScene.ts

TypeScript (SDK 7)import { engine, Transform, TextShape, PointerEvents, InputAction, pointerEventsSystem } from '@dcl/sdk/ecs' import { signedFetch } from '~system/SignedFetch' const API_URL = 'https://zonecoin.zonenations.com/api/index.php/v1/zonecoin' const API_KEY = 'YOUR_API_KEY' const MERCHANT = 'your-merchant-id' // ── Payment Terminal Entity ─────────────────────────────── const terminal = engine.addEntity() Transform.create(terminal, { position: { x: 8, y: 1.5, z: 8 } }) TextShape.create(terminal, { text: '⚡ ZoneCoin Terminal\nClick to interact', fontSize: 3, textColor: { r: 0.4, g: 0.9, b: 1.0, a: 1 } }) PointerEvents.create(terminal, { pointerEvents: [{ eventType: 1, eventInfo: { button: InputAction.IA_POINTER, hoverText: 'Pay with ZoneCoin' } }] }) // ── Click Handler ───────────────────────────────────────── pointerEventsSystem.onPointerDown({ entity: terminal, opts: { button: InputAction.IA_POINTER } }, async () => { try { const res = await signedFetch({ url: `${API_URL}/pay`, init: { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify({ amount: 10, ref: `DCL-${Date.now()}`, merchant: MERCHANT, isFiat: false }) } }) const data = JSON.parse(res.body || '{}') const ts = TextShape.getMutable(terminal) ts.text = data.txId ? `✅ Paid! TX: ${data.txId}` : '❌ Payment failed' } catch (e) { const ts = TextShape.getMutable(terminal) ts.text = '❌ Network error' } } )

🏖️ The Sandbox

L'integration The Sandbox Game Maker fournit un ScriptComponent pour les achats in-game. Utilise HTTPRequest pour les appels API et supporte les flux de paiement declenches par clic avec retour visuel.

Composant Boutique In-Game

TypeScript (Game Maker)// ZoneCoin Integration for The Sandbox Game Maker // Add this as a custom script component import { ScriptComponent, Trigger, HTTPRequest } from '@sandbox/core' const API_URL = 'https://zonecoin.zonenations.com/api/index.php/v1/zonecoin' const API_KEY = 'YOUR_API_KEY' export default class ZoneCoinShop extends ScriptComponent { private apiUrl = API_URL private apiKey = API_KEY onInit() { this.entity.on(Trigger.OnClick, () => this.purchaseItem()) } async purchaseItem() { const request = new HTTPRequest(`${this.apiUrl}/pay`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}` }, body: JSON.stringify({ amount: 50, ref: `SAND-${Date.now()}`, merchant: 'your-merchant', isFiat: false }) }) const res = await request.send() if (res.status === 200) { const data = JSON.parse(res.body) this.showNotification(`✅ Payment OK! TX: ${data.txId}`) this.grantReward() } else { this.showNotification('❌ Payment failed') } } showNotification(msg: string) { // Use The Sandbox notification system this.entity.getComponent('TextRenderer').text = msg } grantReward() { // Grant in-game item/reward to player } }

🔌 Référence Rapide API REST

Toutes les plateformes utilisent la même API REST. Authentification avec HMAC-SHA256.

📊
GET /rate — Taux de change ZC/EUR actuel et donnees de marche
💰
GET /balance/{id} — Recuperer le solde du portefeuille et les infos de devise
💳
POST /pay — Traiter le paiement avec reglement marchand
🔄
POST /transfer — Transfert ZoneCoin peer-to-peer entre portefeuilles
🔍
GET /tx/{id} — Statut de transaction et details de confirmation
🔔
POST /webhook/register — Enregistrer l'endpoint de notification de paiement cote serveur

Prêt à intégrer ?

Choisissez votre plateforme, téléchargez le SDK et commencez à accepter ZoneCoin en quelques minutes.

📥 Module DLL Documentation API E-Commerce