Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15ac91c42f | |||
| 70564ff799 |
@@ -138,7 +138,8 @@ func request(
|
|||||||
path: String,
|
path: String,
|
||||||
body: String = "",
|
body: String = "",
|
||||||
content_type: String = "application/json",
|
content_type: String = "application/json",
|
||||||
require_auth: bool = false
|
require_auth: bool = false,
|
||||||
|
custom_headers: PackedStringArray = PackedStringArray()
|
||||||
) -> Dictionary:
|
) -> Dictionary:
|
||||||
var http := HTTPRequest.new()
|
var http := HTTPRequest.new()
|
||||||
http.timeout = timeout_seconds
|
http.timeout = timeout_seconds
|
||||||
@@ -152,6 +153,8 @@ func request(
|
|||||||
headers.append("Content-Type: " + content_type)
|
headers.append("Content-Type: " + content_type)
|
||||||
if require_auth and has_token():
|
if require_auth and has_token():
|
||||||
headers.append("Authorization: " + bearer_token)
|
headers.append("Authorization: " + bearer_token)
|
||||||
|
for ch in custom_headers:
|
||||||
|
headers.append(ch)
|
||||||
|
|
||||||
var url := _base_url + path
|
var url := _base_url + path
|
||||||
var err := http.request(url, headers, method, body)
|
var err := http.request(url, headers, method, body)
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
extends RefCounted
|
||||||
|
class_name EconomyApiClient
|
||||||
|
## Helper class for AGP Economy REST endpoints (Wallet, Payments, Marketplace, Payouts).
|
||||||
|
##
|
||||||
|
## docs/planning/economy.md US-6 (task cc09c1428892).
|
||||||
|
|
||||||
|
static func get_api_client(node: Node) -> Node:
|
||||||
|
if node.has_node("/root/ApiClient"):
|
||||||
|
return node.get_node("/root/ApiClient")
|
||||||
|
return null
|
||||||
|
|
||||||
|
static func generate_idempotency_key() -> String:
|
||||||
|
return "%d-%d" % [Time.get_unix_time_from_system(), randi() % 1000000]
|
||||||
|
|
||||||
|
# ── Wallet & Currency ──
|
||||||
|
|
||||||
|
static func get_wallet(api: Node) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, "/api/wallet", "", "application/json", true)
|
||||||
|
|
||||||
|
static func get_wallet_transactions(api: Node, limit: int = 50, offset: int = 0) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, "/api/wallet/transactions?limit=%d&offset=%d" % [limit, offset], "", "application/json", true)
|
||||||
|
|
||||||
|
# ── Premium Purchases ──
|
||||||
|
|
||||||
|
static func purchase_credits(api: Node, credits: int, idempotency_key: String = "") -> Dictionary:
|
||||||
|
var custom_headers := PackedStringArray()
|
||||||
|
var key := idempotency_key if idempotency_key != "" else generate_idempotency_key()
|
||||||
|
custom_headers.append("Idempotency-Key: " + key)
|
||||||
|
|
||||||
|
var sess_res: Dictionary = await api.request(
|
||||||
|
HTTPClient.METHOD_POST,
|
||||||
|
"/api/payments/purchase",
|
||||||
|
JSON.stringify({"credits": credits}),
|
||||||
|
"application/json",
|
||||||
|
true,
|
||||||
|
custom_headers
|
||||||
|
)
|
||||||
|
if not sess_res.ok:
|
||||||
|
return sess_res
|
||||||
|
|
||||||
|
var sess_id: String = str(sess_res.data.get("session_id", ""))
|
||||||
|
if sess_id == "":
|
||||||
|
return {"ok": false, "status": 0, "data": null, "detail": "Invalid session returned"}
|
||||||
|
|
||||||
|
return await api.request(
|
||||||
|
HTTPClient.METHOD_POST,
|
||||||
|
"/api/payments/confirm",
|
||||||
|
JSON.stringify({"session_id": sess_id}),
|
||||||
|
"application/json",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Catalog Items & Listings ──
|
||||||
|
|
||||||
|
static func get_catalog_items(api: Node, mine: bool = false, item_type: String = "", limit: int = 50, offset: int = 0) -> Dictionary:
|
||||||
|
var q := "/api/marketplace/items?limit=%d&offset=%d" % [limit, offset]
|
||||||
|
if mine:
|
||||||
|
q += "&mine=true"
|
||||||
|
if item_type != "":
|
||||||
|
q += "&item_type=" + item_type.uri_encode()
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, q, "", "application/json", true)
|
||||||
|
|
||||||
|
static func create_catalog_item(api: Node, name: String, item_type: String, description: String = "", metadata_json: Dictionary = {}) -> Dictionary:
|
||||||
|
var payload := {
|
||||||
|
"name": name,
|
||||||
|
"item_type": item_type,
|
||||||
|
"description": description,
|
||||||
|
"metadata_json": metadata_json
|
||||||
|
}
|
||||||
|
return await api.request(HTTPClient.METHOD_POST, "/api/marketplace/items", JSON.stringify(payload), "application/json", true)
|
||||||
|
|
||||||
|
static func create_listing(api: Node, catalog_item_id: int, price: int) -> Dictionary:
|
||||||
|
var payload := {
|
||||||
|
"catalog_item_id": catalog_item_id,
|
||||||
|
"price": price
|
||||||
|
}
|
||||||
|
return await api.request(HTTPClient.METHOD_POST, "/api/marketplace/listings", JSON.stringify(payload), "application/json", true)
|
||||||
|
|
||||||
|
static func search_listings(api: Node, query: String = "", item_type: String = "", min_price: int = -1, max_price: int = -1, limit: int = 50, offset: int = 0) -> Dictionary:
|
||||||
|
var q := "/api/marketplace/listings?limit=%d&offset=%d" % [limit, offset]
|
||||||
|
if query != "":
|
||||||
|
q += "&q=" + query.uri_encode()
|
||||||
|
if item_type != "":
|
||||||
|
q += "&item_type=" + item_type.uri_encode()
|
||||||
|
if min_price >= 0:
|
||||||
|
q += "&min_price=%d" % min_price
|
||||||
|
if max_price >= 0:
|
||||||
|
q += "&max_price=%d" % max_price
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, q, "", "application/json", false)
|
||||||
|
|
||||||
|
static func get_listing(api: Node, listing_id: int) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, "/api/marketplace/listings/%d" % listing_id, "", "application/json", false)
|
||||||
|
|
||||||
|
static func update_listing(api: Node, listing_id: int, price: int = -1, status: String = "") -> Dictionary:
|
||||||
|
var payload := {}
|
||||||
|
if price > 0:
|
||||||
|
payload["price"] = price
|
||||||
|
if status != "":
|
||||||
|
payload["status"] = status
|
||||||
|
return await api.request(HTTPClient.METHOD_PATCH, "/api/marketplace/listings/%d" % listing_id, JSON.stringify(payload), "application/json", true)
|
||||||
|
|
||||||
|
static func delist_listing(api: Node, listing_id: int) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_DELETE, "/api/marketplace/listings/%d" % listing_id, "", "application/json", true)
|
||||||
|
|
||||||
|
# ── Purchases & Escrow ──
|
||||||
|
|
||||||
|
static func purchase_listing(api: Node, listing_id: int, idempotency_key: String = "") -> Dictionary:
|
||||||
|
var custom_headers := PackedStringArray()
|
||||||
|
var key := idempotency_key if idempotency_key != "" else generate_idempotency_key()
|
||||||
|
custom_headers.append("Idempotency-Key: " + key)
|
||||||
|
|
||||||
|
return await api.request(
|
||||||
|
HTTPClient.METHOD_POST,
|
||||||
|
"/api/marketplace/listings/%d/purchase" % listing_id,
|
||||||
|
"",
|
||||||
|
"application/json",
|
||||||
|
true,
|
||||||
|
custom_headers
|
||||||
|
)
|
||||||
|
|
||||||
|
static func get_purchases(api: Node, role: String = "all", limit: int = 50, offset: int = 0) -> Dictionary:
|
||||||
|
return await api.request(
|
||||||
|
HTTPClient.METHOD_GET,
|
||||||
|
"/api/marketplace/purchases?role=%s&limit=%d&offset=%d" % [role.uri_encode(), limit, offset],
|
||||||
|
"",
|
||||||
|
"application/json",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Creator Balances & Payouts ──
|
||||||
|
|
||||||
|
static func get_creator_balance(api: Node) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, "/api/marketplace/creator/balance", "", "application/json", true)
|
||||||
|
|
||||||
|
static func request_payout(api: Node, amount: int, external_ref: String = "") -> Dictionary:
|
||||||
|
var payload := {"amount": amount}
|
||||||
|
if external_ref != "":
|
||||||
|
payload["external_ref"] = external_ref
|
||||||
|
return await api.request(HTTPClient.METHOD_POST, "/api/payouts", JSON.stringify(payload), "application/json", true)
|
||||||
|
|
||||||
|
static func get_payouts(api: Node, limit: int = 50, offset: int = 0) -> Dictionary:
|
||||||
|
return await api.request(HTTPClient.METHOD_GET, "/api/payouts?limit=%d&offset=%d" % [limit, offset], "", "application/json", true)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
extends SceneTree
|
||||||
|
## Headless smoke test for EconomyApiClient.
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
print("[SMOKE] Starting EconomyApiClient smoke test...")
|
||||||
|
|
||||||
|
var api_script = load("res://scripts/api_client.gd")
|
||||||
|
if not api_script:
|
||||||
|
printerr("[FAIL] Could not load api_client.gd")
|
||||||
|
quit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
var api: Node = api_script.new()
|
||||||
|
root.add_child(api)
|
||||||
|
|
||||||
|
# Verify idempotency key generation
|
||||||
|
var key1: String = EconomyApiClient.generate_idempotency_key()
|
||||||
|
var key2: String = EconomyApiClient.generate_idempotency_key()
|
||||||
|
assert(key1 != "", "Key should not be empty")
|
||||||
|
assert(key1 != key2, "Keys should be unique")
|
||||||
|
print("[PASS] Idempotency key generation: ", key1)
|
||||||
|
|
||||||
|
# Verify method existence on EconomyApiClient
|
||||||
|
var client = EconomyApiClient.new()
|
||||||
|
assert(client.has_method("get_wallet"), "Missing get_wallet")
|
||||||
|
assert(client.has_method("get_wallet_transactions"), "Missing get_wallet_transactions")
|
||||||
|
assert(client.has_method("purchase_credits"), "Missing purchase_credits")
|
||||||
|
assert(client.has_method("get_catalog_items"), "Missing get_catalog_items")
|
||||||
|
assert(client.has_method("create_catalog_item"), "Missing create_catalog_item")
|
||||||
|
assert(client.has_method("create_listing"), "Missing create_listing")
|
||||||
|
assert(client.has_method("search_listings"), "Missing search_listings")
|
||||||
|
assert(client.has_method("get_listing"), "Missing get_listing")
|
||||||
|
assert(client.has_method("update_listing"), "Missing update_listing")
|
||||||
|
assert(client.has_method("delist_listing"), "Missing delist_listing")
|
||||||
|
assert(client.has_method("purchase_listing"), "Missing purchase_listing")
|
||||||
|
assert(client.has_method("get_purchases"), "Missing get_purchases")
|
||||||
|
assert(client.has_method("get_creator_balance"), "Missing get_creator_balance")
|
||||||
|
assert(client.has_method("request_payout"), "Missing request_payout")
|
||||||
|
assert(client.has_method("get_payouts"), "Missing get_payouts")
|
||||||
|
print("[PASS] All EconomyApiClient methods verified")
|
||||||
|
|
||||||
|
api.queue_free()
|
||||||
|
print("[SMOKE] EconomyApiClient smoke test PASSED")
|
||||||
|
quit(0)
|
||||||
Reference in New Issue
Block a user