🔧 Universal Architecture

All platforms connect to the ZoneCoin Joomla backend through a single REST API gateway.

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

⚡ Quick Start

Four steps to integrate ZoneCoin into any platform.

1
Register Grid
Sign up and register your grid/server to receive API credentials.
2
Get API Keys
Generate your API key, secret, and merchant ID from the dashboard.
3
Install SDK
Download the SDK or DLL module for your target platform.
4
Go Live
Test in sandbox, then switch to production — you're ready!

🌐 OpenSimulator

ZoneCoin provides a native C# .NET module that integrates directly into any OpenSimulator grid, enabling real-time wallet management, avatar-to-avatar transfers, land purchases and marketplace transactions — all verified through the ZoneCoin PoA blockchain.

💰
Balance QueryReal-time wallet balance via REST API with multi-currency support.
🔄
P2P TransferInstant avatar-to-avatar transfers with cryptographic verification.
🏠
Land PaymentAutomated land purchase and rental payments with escrow support.
🛒
MarketplaceIn-world object purchasing with merchant settlement and receipts.

📥 Download OpenSim Module (DLL)

🔵 Second Life

Integrate ZoneCoin into Second Life using LSL scripts. Create payment terminals, balance checkers and automated vendors that connect to the ZoneCoin REST API via HTTP requests.

Payment Terminal

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 ], ""); } }

Balance Checker

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

The ZoneCoin Unity SDK provides a MonoBehaviour-based singleton with full async/coroutine support. Handle payments, balance queries and peer transfers with automatic HMAC-SHA256 request signing.

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; } } }

Usage Example

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

The Unreal Engine integration uses a GameInstanceSubsystem exposing BlueprintCallable functions for payments, balance queries and transfers. All requests are HMAC-signed and processed asynchronously.

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

The Godot 4 ZoneCoin SDK is a GDScript autoload singleton using signals for async communication. Supports payments, balance queries and transfers with built-in HMAC-SHA256 signing 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

The Minecraft ZoneCoin plugin integrates with Spigot and Paper servers. Players use /zcbalance and /zcpay to manage wallets. Supports async HTTP calls and HMAC authentication.

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

The Roblox ZoneCoin SDK is a server-side ModuleScript that uses HttpService for API communication. Supports balance queries, payments and peer transfers with pcall error handling.

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

Server Script — Shop Handler

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

The VRChat integration uses UdonSharp to create interactive payment terminals in VRChat worlds. Leverages VRCStringDownloader for API communication and manual sync for multi-user state.

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

Integrate ZoneCoin into Decentraland scenes using SDK 7. Payment terminals are created as interactive entities using PointerEvents and signedFetch for authenticated API calls.

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

The Sandbox integration provides a ScriptComponent for in-game purchases. Uses HTTPRequest for API calls and supports click-triggered payment flows with visual feedback.

In-Game Shop Component

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 } }

🔌 REST API Quick Reference

All platforms use the same REST API. Authenticate with HMAC-SHA256.

📊
GET /rate — Current ZC/EUR exchange rate and market data
💰
GET /balance/{id} — Retrieve wallet balance and currency info
💳
POST /pay — Process payment with merchant settlement
🔄
POST /transfer — Peer-to-peer ZoneCoin transfer between wallets
🔍
GET /tx/{id} — Transaction status and confirmation details
🔔
POST /webhook/register — Register server-side payment notification endpoint

Ready to integrate?

Choose your platform, download the SDK, and start accepting ZoneCoin in minutes.

📥 DLL Module API Docs E-Commerce