Passerelle Universelle Chaque Métavers, Une Devise
Intégrez ZoneCoin dans n'importe quel monde virtuel, moteur de jeux ou métavers avec notre API REST universelle et nos SDK prêts à l'emploi.
🔧 Architecture Universelle
Toutes les plateformes se connectent au backend Joomla de ZoneCoin via une passerelle API REST unique.
⚡ Démarrage Rapide
Quatre étapes pour intégrer ZoneCoin dans n'importe quelle plateforme.
🌐 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.
🔵 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
// 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
// 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
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
// 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
#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
#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
extends 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
name: 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: opZoneCoinPlugin.java
package 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)
-- 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 ZoneCoinScript Serveur — Gestionnaire de Boutique
-- 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)
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
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
// 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.
Prêt à intégrer ?
Choisissez votre plateforme, téléchargez le SDK et commencez à accepter ZoneCoin en quelques minutes.