2 Commits a109527369 ... 32a83b6aa3

Autor SHA1 Mensaje Fecha
  ljoaquim 32a83b6aa3 genial cli hace 2 semanas
  ljoaquim 9732a3cedd genial cli hace 2 semanas
Se han modificado 3 ficheros con 442 adiciones y 2 borrados
  1. 0 1
      bin/genial-cli
  2. 437 0
      bin/genial-cli
  3. 5 1
      libs/BashExecutor.php

+ 0 - 1
bin/genial-cli

@@ -1 +0,0 @@
-Subproject commit ee0a402a2b12c23e471b01460752bf838a6e80c7

+ 437 - 0
bin/genial-cli

@@ -0,0 +1,437 @@
+#!/usr/bin/env -S /usr/bin/node --no-deprecation
+
+require("dotenv").config({ quiet: true });
+const decimal = require("decimal.js");
+const randomUUID = require("crypto").randomUUID;
+const DEBUG = true;
+
+const AUTH_URL =
+  "https://genial-arquitetura-authentication.homolog.api.genial.systems";
+const COREBK_URL =
+  "https://genial-arquitetura-corebank.homolog.api.genial.systems";
+const BAAS_URL = "https://gerador-arquitetura-baas.homolog.api.genial.systems";
+const OPS_URL = "https://genial-baas-operations.homolog.api.genial.systems";
+
+async function main() {
+  const args = process.argv.slice(2);
+  if (args.length === 0) return help();
+  const cmd = args[0];
+  const rest = args.slice(1);
+
+  try {
+    switch (cmd) {
+      case "token": {
+        const [ok, tok] = await getToken();
+        if (!ok) return fail(tok);
+        console.log(tok);
+        break;
+      }
+      case "balance": {
+        await Balance(rest[0]);
+        break;
+      }
+      case "getkey": {
+        if (rest.length < 1) return fail("Usage: getkey <pixKey>");
+        await getKey(rest[0]);
+        break;
+      }
+      case "decode": {
+        if (rest.length < 1) return fail("Usage: decodeQrcode <qrcode>");
+        await decodeQrcode(rest[0]);
+        break;
+      }
+      case "payQr": {
+        if (rest.length < 2)
+          return fail("Usage: payQr <qrCodeCopyPaste>  <value>");
+        console.log(rest[0], rest[1]);
+        await PayQr(rest[0], rest[1]);
+        break;
+      }
+      case "qrcodeStatic": {
+        await qrcodeStatic();
+        break;
+      }
+      case "paykey": {
+        if (rest.length < 2) return fail("Usage: payInit <pixKey> <value>");
+        await PayInit(rest[0], rest[1]);
+        break;
+      }
+      case "qrcodedynamic": {
+        if (rest.length < 1) return fail("Usage: qrcode  <value>");
+        await QrCodeDynamic(rest[0]);
+        break;
+      }
+      case "statusIn": {
+        //not working on my machine
+        if (rest.length < 1) return fail("Usage: statusIn <transactionId>");
+        await StatusIn(rest[0]);
+        break;
+      }
+      case "statusOut": {
+        //not working on my machine
+        if (rest.length < 1) return fail("Usage: statusOut <e2e>");
+        await StatusOut(rest[0]);
+        break;
+      }
+      case "refund": {
+        if (rest.length < 1) return fail("Usage: refund <eventId> <value>");
+        await Refund(rest[0], rest[1]);
+        break;
+      }
+      default:
+        help();
+        return;
+    }
+  } catch (e) {
+    fail(e.message || e);
+    help();
+    return;
+  }
+}
+
+function help() {
+  console.log(`Usage: genial-cli <command> [args]
+Commands:
+  token
+  balance <accountNumber>
+  getkey <accountNumber> <pixKey>
+  statusOut <instantPaymentId> <YYYY-MM-DD>
+  statusIn <transactionId>
+  qrcode <accountNumber> <value>
+  payInit [value] [pixKey]
+  pay <idempotencyId>
+  payQr <accountNumber> <value> <qrCodeCopyPaste>
+  refund <eventId> <value>
+  preview <emv>
+  boletoPay <taxId> <line> [description] [uuid]
+  boletoGet <id>
+`);
+  process.exit(1);
+}
+
+function fail(msg) {
+  console.error("Error:", msg);
+  process.exit(1);
+}
+
+/* =============== Auth =============== */
+
+async function getToken() {
+  const url = `${AUTH_URL}/v1/authentication`;
+  const options = {
+    method: "POST",
+    headers: {
+      basic: process.env.TOKEN_BASIC,
+      "X-ORIGEM": process.env.TOKEN_XORIGEM,
+    },
+  };
+  try {
+    if (DEBUG) {
+      console.log("\nDEBUG callApi:", options.method, url);
+    }
+    const res = await fetch(url, options);
+    const text = await res.text();
+    if (DEBUG) {
+      console.log("DEBUG getToken status:", res.status);
+      console.log("DEBUG getToken body:", text);
+    }
+    if (res.status < 200 || res.status >= 300)
+      return [false, { status: res.status, body: text }];
+    const json = JSON.parse(text);
+    if (!json.token) return [false, "No token in response"];
+    return [true, json.token];
+  } catch (e) {
+    return [false, e.message];
+  }
+}
+
+async function callApi(method, url, body) {
+  const [ok, token] = await getToken();
+  if (!ok) return [false, token];
+  const headers = {
+    Authorization: token,
+    "X-ORIGEM": process.env.TOKEN_XORIGEM,
+  };
+  const options = { method, headers };
+
+  if (method.toUpperCase() === "POST") {
+    headers["Content-Type"] = "application/json";
+    options.body = body ? JSON.stringify(body) : "";
+  }
+
+  if (DEBUG) {
+    console.log("\nDEBUG callApi:", method, url);
+    if (body) console.log("DEBUG body:", JSON.stringify(body));
+  }
+
+  try {
+    const res = await fetch(url, options);
+    const text = await res.text();
+    if (DEBUG) {
+      console.log("DEBUG status:", res.status);
+      console.log("DEBUG raw:", text);
+    }
+    if (res.status < 200 || res.status >= 300)
+      return [false, { status: res.status, body: text }];
+    try {
+      return [true, JSON.parse(text)];
+    } catch {
+      return [true, text];
+    }
+  } catch (e) {
+    return [false, e.message];
+  }
+}
+/**
+ * @description
+ * account 2 is me the user
+ * account 1 is smartpay the receiver of the payment
+ */
+/* =============== 1) Balance =============== done*/
+
+async function Balance(accountNumber) {
+  //INFO: they share 2 acount to test, but idk if on prod only is gonna be one. ignore this, after i know the answer i will delete
+  let account = process.env.ACCOUNT1;
+  const url = `${COREBK_URL}/v1/core-banking/balance/1/${account}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 2) Dict Key =============== done*/
+
+async function getKey(pixKey) {
+  const url = `${BAAS_URL}/v2/pix/key-account-partner/1/${process.env.ACCOUNT1}/${pixKey}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== DECODE QRCODE =============== done*/
+async function decodeQrcode(qrcode) {
+  const url = `https://gerador-arquitetura-baas.homolog.api.genial.systems/v1/pix/qrcode-decode?qrcode=${encodeURIComponent(
+    qrcode
+  )}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 8) Pay by QR =============== done*/
+
+async function PayQr(qrCodeCopyPaste, value) {
+  const url = `${BAAS_URL}/v1/pix/copy-paste`;
+  /**
+   * debit is our account that is going to pay the qr code, need prod keys to do it correctly
+   */
+  const body = {
+    qrCodeCopyPaste,
+    value: Number(value),
+    debit: {
+      name: process.env.ACCOUNT2_NAME,
+      agency: 1,
+      account: process.env.ACCOUNT2,
+      accountType: "CACC",
+    },
+  };
+  console.log(body);
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 2) QR Code Static =============== done*/
+async function qrcodeStatic() {
+  const url = `${BAAS_URL}/v1/pix/qrcode-static/`;
+  const body = {
+    accountHolderName: process.env.ACCOUNT1_NAME,
+    addressingKey: {
+      key: process.env.ACCOUNT1_KEY,
+      type: "EVP",
+    },
+    account: process.env.ACCOUNT1,
+    agency: 1,
+    // additionalInformation: "Pagamento de teste genial-cli" /**@optional */,
+    // value: 10.0, /**@optional */
+  };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 3) Status OUT (PAC008) =============== NEED TO CHECK*/
+async function StatusOut(e2e) {
+  const url = `${OPS_URL}/v1/cashout/${encodeURIComponent(e2e)}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 4) Status IN (PAC002) =============== NEED TO CHECK*/
+
+async function StatusIn(transactionId) {
+  const url = `${OPS_URL}/v1/cashin/end-to-end-id/${encodeURIComponent(
+    transactionId
+  )}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 6) Init Pix by Key (PAC008 Start) ===============  done */
+/**
+ *
+ * @param pixKey
+ * @param value
+ * @returns confirmed idempotencyId [@description we need to create the idempotencyId on our side to confirm the payment later  ]
+ */
+async function PayInit(pixKey, value) {
+  const url = `${BAAS_URL}/v1/pix/send/initialization`;
+  const body = {
+    debit: {
+      name: process.env.ACCOUNT1_NAME,
+      agency: 1,
+      account: process.env.ACCOUNT1,
+      accountType: "CACC",
+    },
+    credit: {
+      key: pixKey,
+    },
+    value: String(value),
+    idempotencyId: randomUUID(),
+  };
+  var [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+  if (out?.data?.idempotencyId) {
+    console.log("idempotencyId:", out.data.idempotencyId);
+  }
+  var [ok, reply] = await paykey(out.data.idempotencyId);
+  if (!ok) return fail(reply);
+  console.dir(reply, { depth: null });
+}
+
+/* =============== 7) Pay by Idempotency (PAC008) =============== done */
+/**
+ *
+ * @param idempotencyId
+ * @param payinfo
+ * @returns
+ */
+async function paykey(idempotencyId, payinfo) {
+  const url = `${BAAS_URL}/v1/pix/send/confirmed`;
+  const body = {
+    idempotencyId: idempotencyId,
+    ignorePaymentDuplication: false,
+    // additionalInformation: payinfo, /**@optional */
+  };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  if (DEBUG) console.log("Payment confirmed for idempotencyId:", idempotencyId);
+  return [true, out];
+}
+
+/* =============== 9) Refund (PAC004) =============== worked*/
+
+async function Refund(eventId, value) {
+  const url = `${BAAS_URL}/v1/pix/return`;
+  const body = {
+    value: value,
+    returnReasonCode: "MD06",
+    eventId: eventId,
+  };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+async function QrCodeDynamic(value) {
+  const url = `${BAAS_URL}/v2/qrcode/dynamic?agency=1&account=${process.env.ACCOUNT1}`;
+  const body = {
+    items: [
+      {
+        addressingKey: {
+          key: process.env.ACCOUNT1_KEY,
+          type: "EVP",
+        },
+        accountHolderName: process.env.ACCOUNT1_NAME,
+        accountHolderCity: "SAO PAULO",
+        dynamicQRCodeType: "IMMEDIATE",
+        immediate: {
+          expiration: 36000,
+          paymentValue: {
+            documentValue: value,
+            showPaymentValueInQrCode: true,
+            //"cashback": {
+            //"cashbackType": "WITHDRAW",
+            //"value": 0,
+            //"allowValueChange": true,
+            //"withdrawProviders": {
+            //"agentModality": "string",
+            //"serviceProvider": "string"
+            //}
+            //}
+          },
+          payerInformation: {
+            cpfCnpj: "37812511871",
+            name: "gustavo lopes",
+            validatePayerInformation: true,
+          },
+          receiverInformation: {
+            taxId: process.env.DOCUMENT_NUMBER,
+            name: process.env.ACCOUNT1_NAME,
+            tradeName: process.env.ACCOUNT1_NAME,
+          },
+        },
+        payerRequestInformation: "TESTE SOLICITAÇÃO AO PAYER",
+        //"additionalInformation": [
+        //{
+        //"name": "string",
+        //"content": "string",
+        //"showToPayer": true
+        //}
+        //],
+        //"itemId": "string",
+        //"receiverTxId": "CTXXXXXXXXXXXX999999995800"
+      },
+    ],
+  };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+/* =============== 10) Preview Pagamento (BOLETO) =============== did not test*/
+
+async function Preview(emv) {
+  const url = `${BAAS_URL}/v1/payments/preview`;
+  const body = { emv };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 11) Boleto Pay =============== did not test*/
+
+async function BoletoPay(taxId, line, description, uuid) {
+  const url = `${BAAS_URL}/v1/payments/boleto`;
+  const body = {
+    taxId,
+    description: description || "Pagamento de boleto",
+    line,
+    uuid: uuid || `BOL-${Date.now()}`,
+  };
+  const [ok, out] = await callApi("POST", url, body);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+/* =============== 12) Boleto Get by Id =============== did not test*/
+
+async function BoletoGet(id) {
+  const url = `${BAAS_URL}/v1/payments/boleto/${encodeURIComponent(id)}`;
+  const [ok, out] = await callApi("GET", url);
+  if (!ok) return fail(out);
+  console.dir(out, { depth: null });
+}
+
+main();

+ 5 - 1
libs/BashExecutor.php

@@ -50,7 +50,11 @@ class BashExecutor
             fclose($pipe);
         }
 
-        $exitCode = proc_close($process);
+        $lastStatusExitCode = $status['exitcode'] ?? null;
+        $closeExitCode = proc_close($process);
+        $exitCode = ($lastStatusExitCode === null || $lastStatusExitCode === -1)
+            ? $closeExitCode
+            : $lastStatusExitCode;
 
         return [
             "exitCode" => $exitCode,