Code
- Attach a new script to the ‘insertcoin’ Node (Root Node), name it ‘insertcoin.gd’
- Click the ‘insertcoin’ node (Button) under the Node tab (this tab is next/behind the Inspector on the right hand side of Godot) and double click the ‘Pressed()’ signal, select the ‘insertcoin’ Node (root Node) and click 'Connect’ to automatically create the ‘_on_insertcoin_pressed method’. The connection wil be created in the Script ‘insertcoin.gd’ of the ‘insertcoin’ Node.
- Open the ‘insertcoin.gd’ script, remove all contents (ctrl-a, ctrl-x) and copy-paste gdscript: insertcoin.gd to replace everything (copy here from the turorial and paste inside godot script editor).
gdscript: insertcoin.gd
extends Node
func _ready():
pass # Replace with function body.
func _on_insertcoin_pressed():
get_node("invoice").show()
- Attach a new script to the ‘qrcode’ Node (Sprite), name it ‘qrcode.gd’.
- Click the ‘Button’ Node (Button) under the Node tab (next/behind the Inspector) and double click the ‘Pressed()’ Signal, select the ‘qrcode’ Node (Sprite) and click ‘Connect’ to create the ‘_on_Button_Pressed’ Method.
- Open the ‘qrcode.gd’ script by clicking on the scroll icon, next to the ‘qrcode’ Node (Sprite), delete all it’s contents (including the Method you just created) and copy-paste the following code:
gdscript: qrcode.gd
extends Sprite
const qrpng = "payreq_qrcode.png"
const path = "C:/Users/your/location/of/godot/insertcoins/"
func _ready():
#unused
pass
func generate_qrcode(payreq):
var output = []
#generate rcode
OS.execute(path + 'zint.exe', ['-b', '58', '-o', path + qrpng, '--vers=15', '--scale', '2', '-d', payreq] ,true ,output)
#to test the generation of the qrcode, uncomment the next line and check if zint is in this path
#print(path)
func _on_Button_pressed():
var httpr = load("res://HTTPRequest.gd").new()
var jsonh = load("res://jsonhandler.gd").new()
#generate new invoice
var ni = httpr.new_invoice()
#test what happens by uncommenting the next line:
#print(ni)
#construct the new invoice data dictionary
var invoice = jsonh.new_invoice(ni)
#generate QR COde
generate_qrcode(invoice['payreq'])
#show qrcode
var imageTexture = ImageTexture.new()
var dynImage = Image.new()
dynImage.load(path + qrpng)
imageTexture.create_from_image(dynImage)
self.texture = imageTexture
#print(invoice['id'])
#show invoice status
get_node("../Button").disabled = true
get_node("../Button").text = "Connecting..."
get_node("../Label").text = "scan QR code with your LN wallet."
#pause
yield(get_tree().create_timer(1.1),"timeout")
#var invoices = httpr.get_invoices()
#start polling this invoice's status
var invid = invoice['id']
invoice_poller(httpr, jsonh, invid)
func invoice_poller(httpr, jsonh, id, count = 1023):
#get status for this invoice (try 1023 times (max))
count -= 1
#request data and process json
var invoices = httpr.get_invoices()
var invoice = jsonh.poll_invoice(invoices, id)
#print("status:" + invoice['status'] + " Expires: " + str(invoice['expires_at']) + " Conn timeout: " + str(count))
if count == 0 or invoice['status'] == 'paid':
#print(invoice)
#print("status:" + invoice['status'] + " Expires: " + str(invoice['expires_at']) + " Conn timeout: " + str(count))
if count == 0:
print("LN server timeout")
if invoice['status'] == 'paid':
if int(invoice['msatoshi_received']) >= 10000:
get_node("../Label").text = invoice['msatoshi_received'] + " sats received. Have fun!"
get_tree().change_scene("res://game.tscn")
else:
get_node("../Label").text = "Payment received. Insufficient funds :-("
#print("invoice expiry date: " + str(invoice['expires_at']))
return
else:
yield(get_tree().create_timer(2.5),"timeout")
get_node("../Label").text = "Scan QR code with your LN wallet. Conn timeout: " + str(count) + " (" + str(invoice['status']) + ")"
#recurse: see if invoice is paid yet
invoice_poller(httpr, jsonh, id, count)
- Use zint.exe from this project or get a version for your system here: zint
-
In the ‘qrcode.gd’ Script set the Constant (const) on line 3 to the path of your godot project folder (you’ve remembered this in Step 1 - initial setup).
- Add a HTTPRrequest Child Node under ‘insertcoin’ (root Node).
- Attach a script to the ‘HTTPRequest’ Node (HTTPRequest) and name it ‘HTTPRequest.gd’.
- Attach a new script to this ‘HTTPRequest’ Node, delete it’s contents, and copy past the following code:
gdscript: HTTPRequest.gd
extends HTTPRequest
# This simple class can do HTTP requests
const host = "10.0.0.1"
const port = 9112
func _ready():
#output = get_invoices()
pass
func get_info():
return request_data("info")
func get_invoices():
return request_data("invoices")
func new_invoice():
#return json
return request_data("invoice")
func request_data(type):
var err = 0
var http = HTTPClient.new() # Create the Client.
err = http.connect_to_host(host, port) # Connect to host/port.
assert(err == OK) # Make sure connection was OK.
# Wait until resolved and connected.
while http.get_status() == HTTPClient.STATUS_CONNECTING or http.get_status() == HTTPClient.STATUS_RESOLVING:
http.poll()
#print("Connecting...")
OS.delay_msec(500)
assert(http.get_status() == HTTPClient.STATUS_CONNECTED) # Could not connect
# Some headers
var headers = [
"User-Agent: Pirulo/1.0 (Godot)",
"Content-Type: application/json",
"Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", #base64 encoded string "Token:Secret" ! DO NOT USE in shared compiled program (add hashing)
"Accept: */*"
]
var query = '{"msatoshi":10000,"metadata":{"customer_id":9999,"products":[1,1]},"description":"yourgame invoice"}'
if type == "invoice":
err = http.request(HTTPClient.METHOD_POST, "/" + type, headers, query) # Request a new invoice using POST
else:
err = http.request(HTTPClient.METHOD_GET, "/" + type, headers) # Request info or list of invoices using GET
assert(err == OK) # Make sure all is OK.
while http.get_status() == HTTPClient.STATUS_REQUESTING:
# Keep polling for as long as the request is being processed.
http.poll()
#print("Requesting...")
if not OS.has_feature("web"):
OS.delay_msec(500)
else:
# Synchronous HTTP requests are not supported on the web,
# so wait for the next main loop iteration.
yield(Engine.get_main_loop(), "idle_frame")
assert(http.get_status() == HTTPClient.STATUS_BODY or http.get_status() == HTTPClient.STATUS_CONNECTED) # Make sure request finished well.
#print("response? ", http.has_response()) # Site might not have a response.
if http.has_response():
# If there is a response...
headers = http.get_response_headers_as_dictionary() # Get response headers.
#print("code: ", http.get_response_code()) # Show response code.
#print("**headers:\\n", headers) # Show headers.
# Getting the HTTP Body
if http.is_response_chunked():
# Does it use chunks?
print("Response is Chunked")
#else:
# Or just plain Content-Length
var bl = http.get_response_body_length()
print("Response Length: ",bl)
# This method works for both anyway
var rb = PoolByteArray() # Array that will hold the data.
while http.get_status() == HTTPClient.STATUS_BODY:
# While there is body left to be read
http.poll()
var chunk = http.read_response_body_chunk() # Get a chunk.
if chunk.size() == 0:
# Got nothing, wait for buffers to fill a bit.
OS.delay_usec(1000)
else:
rb = rb + chunk # Append to read buffer.
var text = rb.get_string_from_ascii()
#print("bytes got: ", rb.size())
return text
- On line 3 and 4 of ‘HTTPRequest.gd’ set your host (server-ip) and it’s port to what you have remembered earlier.
- Generate a base64 encoded string from your ‘api-token’ and your ‘secretpassword’, concatenated using a semicolon like this: login:password. You remembered this during the installation of Lighting Charge in the requirements section. Use a handy online tool like this base64encode (choose ‘encode’) or a command-line if you know what you’re doing.
-
On line 39 replace the X-es with your base64 encoded access credentials. Be careful, do not use this in a production version of your game without understanding the security implications, people could decompile your executable, find these credentials, gain access to your Lightning Charge server and see your invoices.
- Add a Child Node (Node) under ‘insertcoin’ (Root Node) and rename it “jsonhandler”. (You’ll find it in the node creation window when you remove your previous search string, it’s the top one)
- Attach a new script, name it ‘jsonhandler.gd’, remove all it’s contents and copy-paste this code:
gdscript: jsonhandler.gd
extends Node
#class handles json
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func get_invoice(json, id):
#get a specific invoice as array
var invoices = JSON.parse(json).result
#print("parsed json: ")
#print(invoices)
if typeof(invoices) == TYPE_ARRAY:
#loop through list of invoices and find the one requested
for i in range(invoices.size()):
if invoices[i]['id'] == id:
return invoices[i] #return dictionary
else:
print("unexpected result")
func new_invoice(json):
#process json from new invoice
var invoice = JSON.parse(json).result
if typeof(invoice) == TYPE_DICTIONARY:
return invoice #return dictionary
else:
print("not a dictionary")
func poll_invoice(json, id):
var invoice = get_invoice(json, id)
return invoice
- From the Godot menu click ‘Scene > New Scene’, name it “game” and Save as ‘game.tscn’.
- From the menu select ‘Project > Project settings’. In the project window under the ‘application’ category click the ‘Run’ item. Choose ‘insertcoins.tscn’ as your main Scene using the folder icon on the right.
- Close the window.