2023-02-06 02:23:00 +01:00
|
|
|
#include <Arduino.h>
|
2023-02-08 21:34:52 +01:00
|
|
|
#include <ArduinoJson.h>
|
2023-02-06 02:23:00 +01:00
|
|
|
#include <WiFiClient.h>
|
|
|
|
#include <HTTPClient.h>
|
|
|
|
|
2023-04-20 16:26:43 +02:00
|
|
|
String byteArrayAsHexString(byte* buffer, byte bufferSize) {
|
2023-02-08 16:42:47 +01:00
|
|
|
String s = "";
|
|
|
|
for (byte i = 0; i < bufferSize; i++) {
|
|
|
|
s += (buffer[i] < 0x10 ? "0" : "");
|
|
|
|
s += String(buffer[i], HEX);
|
|
|
|
}
|
|
|
|
return s;
|
2023-02-06 02:23:00 +01:00
|
|
|
}
|
|
|
|
|
2023-02-08 21:34:52 +01:00
|
|
|
String CentsToEuros(long cents){
|
|
|
|
char euros[16];
|
|
|
|
sprintf(euros, "%s%ld,%02ld", cents < 0 ? "-" : "", abs(cents / 100), abs(cents % 100));
|
|
|
|
return euros;
|
|
|
|
}
|
|
|
|
|
2023-02-06 02:23:00 +01:00
|
|
|
unsigned long cooldownSecondsRemaining(unsigned long timeout, unsigned long timer) {
|
2023-02-08 16:42:47 +01:00
|
|
|
return (timeout - (millis() - timer)) / 1000 + 1;
|
2023-02-06 02:23:00 +01:00
|
|
|
}
|
|
|
|
|
2023-02-08 21:34:52 +01:00
|
|
|
String getIdempotencyKey(){
|
|
|
|
const int len = 16;
|
|
|
|
byte buf[len];
|
|
|
|
esp_fill_random(buf, len);
|
|
|
|
return byteArrayAsHexString(buf, len);
|
|
|
|
}
|
|
|
|
|
2023-02-10 03:09:50 +01:00
|
|
|
String splitString(const String& data, char separator, int index) {
|
|
|
|
unsigned int maxIndex = data.length() - 1;
|
2023-02-08 16:42:47 +01:00
|
|
|
int strIndex[] = {0, -1};
|
2023-02-10 03:09:50 +01:00
|
|
|
int found = 0;
|
2023-02-06 02:23:00 +01:00
|
|
|
|
2023-02-08 16:42:47 +01:00
|
|
|
for (int i = 0; i <= maxIndex && found <= index; i++) {
|
|
|
|
if (data.charAt(i) == separator || i == maxIndex) {
|
|
|
|
found++;
|
|
|
|
strIndex[0] = strIndex[1] + 1;
|
|
|
|
strIndex[1] = (i == maxIndex) ? i + 1 : i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
|
2023-02-06 02:23:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-20 16:26:43 +02:00
|
|
|
String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId) {
|
2023-02-08 21:34:52 +01:00
|
|
|
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/link";
|
2023-02-08 16:42:47 +01:00
|
|
|
http->begin(*wifi, finalRequestUrl.c_str());
|
2023-02-08 21:34:52 +01:00
|
|
|
http->addHeader("Content-Type", "application/json");
|
|
|
|
int httpResponseCode = http->PUT("");
|
|
|
|
if (httpResponseCode == 304)
|
|
|
|
return "E:Already registered.";
|
|
|
|
if (httpResponseCode == 200 || httpResponseCode == 404) {
|
2023-02-08 16:42:47 +01:00
|
|
|
String payload = http->getString();
|
|
|
|
http->end();
|
2023-02-08 21:34:52 +01:00
|
|
|
Serial.println(payload);
|
|
|
|
StaticJsonDocument<256> json;
|
|
|
|
DeserializationError error = deserializeJson(json, payload.c_str());
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
Serial.println(error.c_str());
|
|
|
|
return String("E:JsonError:") + httpResponseCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* status = json["status"];
|
|
|
|
|
|
|
|
if (strcmp(status, "success") != 0) {
|
|
|
|
const char* message = json["message"];
|
|
|
|
return String("E:") + message;
|
|
|
|
}
|
|
|
|
|
|
|
|
JsonObject data = json["data"];
|
|
|
|
const char* nickname = data["nickname"];
|
|
|
|
long balance = data["balance"];
|
|
|
|
|
|
|
|
return String("S:") + nickname + ":" + CentsToEuros(balance);
|
2023-02-08 16:42:47 +01:00
|
|
|
}
|
|
|
|
http->end();
|
|
|
|
if (httpResponseCode > 0) {
|
|
|
|
return String("E:HTTP Error ") + httpResponseCode;
|
|
|
|
}
|
|
|
|
return String("E:Internal Error ") + httpResponseCode;
|
2023-02-06 02:23:00 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 16:26:43 +02:00
|
|
|
String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId) {
|
2023-02-08 21:34:52 +01:00
|
|
|
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/balance";
|
2023-02-08 16:42:47 +01:00
|
|
|
http->begin(*wifi, finalRequestUrl.c_str());
|
2023-02-08 21:34:52 +01:00
|
|
|
http->addHeader("Content-Type", "application/json");
|
2023-02-08 16:42:47 +01:00
|
|
|
int httpResponseCode = http->GET();
|
2023-02-08 21:34:52 +01:00
|
|
|
if (httpResponseCode == 200 || httpResponseCode == 404) {
|
2023-02-08 16:42:47 +01:00
|
|
|
String payload = http->getString();
|
|
|
|
http->end();
|
2023-02-08 21:34:52 +01:00
|
|
|
Serial.println(payload);
|
|
|
|
StaticJsonDocument<256> json;
|
|
|
|
DeserializationError error = deserializeJson(json, payload.c_str());
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
Serial.println(error.c_str());
|
|
|
|
return "E:JsonError";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* status = json["status"];
|
|
|
|
|
|
|
|
if (strcmp(status, "success") != 0) {
|
|
|
|
const char* message = json["message"];
|
|
|
|
return String("E:") + message;
|
|
|
|
}
|
|
|
|
|
|
|
|
JsonObject data = json["data"];
|
|
|
|
const char* nickname = data["nickname"];
|
|
|
|
long balance = data["balance"];
|
|
|
|
|
|
|
|
return String("S:") + nickname + ":" + CentsToEuros(balance);
|
2023-02-08 16:42:47 +01:00
|
|
|
}
|
|
|
|
http->end();
|
|
|
|
if (httpResponseCode > 0) {
|
|
|
|
return String("E:HTTP Error ") + httpResponseCode;
|
|
|
|
}
|
|
|
|
return String("E:Internal Error ") + httpResponseCode;
|
2023-02-06 02:23:00 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 16:26:43 +02:00
|
|
|
String cardTransaction(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& amount) {
|
2023-02-08 21:34:52 +01:00
|
|
|
String idempotencyKey = getIdempotencyKey();
|
|
|
|
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/transaction/" + idempotencyKey + "?amount=" + amount;
|
2023-02-08 16:42:47 +01:00
|
|
|
http->begin(*wifi, finalRequestUrl.c_str());
|
2023-02-08 21:34:52 +01:00
|
|
|
http->addHeader("Content-Type", "application/json");
|
|
|
|
int httpResponseCode = http->PUT("");
|
|
|
|
if (httpResponseCode == 200 || httpResponseCode == 404 || httpResponseCode == 412) {
|
2023-02-08 16:42:47 +01:00
|
|
|
String payload = http->getString();
|
|
|
|
http->end();
|
2023-02-08 21:34:52 +01:00
|
|
|
Serial.println(payload);
|
|
|
|
StaticJsonDocument<256> json;
|
|
|
|
DeserializationError error = deserializeJson(json, payload.c_str());
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return "E:JsonError";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* status = json["status"];
|
|
|
|
|
|
|
|
if (strcmp(status, "success") != 0) {
|
|
|
|
const char* message = json["message"];
|
|
|
|
return String("E:") + message;
|
|
|
|
}
|
|
|
|
|
|
|
|
JsonObject data = json["data"];
|
|
|
|
const char* nickname = data["nickname"];
|
|
|
|
long balance = data["balance"];
|
|
|
|
|
|
|
|
return String("S:") + nickname + ":" + CentsToEuros(balance);
|
2023-02-08 16:42:47 +01:00
|
|
|
}
|
|
|
|
http->end();
|
|
|
|
if (httpResponseCode > 0) {
|
|
|
|
return String("E:HTTP Error ") + httpResponseCode;
|
|
|
|
}
|
|
|
|
return String("E:Internal Error ") + httpResponseCode;
|
|
|
|
}
|