|
|
@@ -0,0 +1,454 @@
|
|
|
+#!/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 "decodeQrcode": {
|
|
|
+ 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 < 1) return fail("Usage: payInit <pixKey> <value>");
|
|
|
+ await PayInit(rest[0], rest[1]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // done up
|
|
|
+ case "qrcodedynamic": {
|
|
|
+ // if (rest.length <2) return fail("Usage: qrcode <value>");
|
|
|
+ await QrCodeDynamic(rest[1]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "statusIn": {
|
|
|
+ if (rest.length < 1) return fail("Usage: statusIn <transactionId>");
|
|
|
+ await StatusIn(rest[0]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "statusOut": {
|
|
|
+ if (rest.length < 1) return fail("Usage: statusOut <e2e>");
|
|
|
+ await StatusOut(rest[0]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // case "paykey": {
|
|
|
+ // if (rest.length < 1) return fail("Usage: pay <idempotencyId>");
|
|
|
+ // await paykey(rest[0]);
|
|
|
+ // break;
|
|
|
+ // } just use it for test
|
|
|
+ case "refund": {
|
|
|
+ if (rest.length < 1) return fail("Usage: refund <eventId> <value>");
|
|
|
+ await Refund(rest[0], rest[1]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "preview": {
|
|
|
+ if (rest.length < 1) return fail("Usage: preview <emv>");
|
|
|
+ await Preview(rest[0]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "boletoPay": {
|
|
|
+ if (rest.length < 2)
|
|
|
+ return fail("Usage: boletoPay <taxId> <line> [description] [uuid]");
|
|
|
+ await BoletoPay(rest[0], rest[1], rest[2], rest[3]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "boletoGet": {
|
|
|
+ if (rest.length < 1) return fail("Usage: boletoGet <id>");
|
|
|
+ await BoletoGet(rest[0]);
|
|
|
+ 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 {
|
|
|
+ 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.ACCOUNT2_NAME,
|
|
|
+ agency: 1,
|
|
|
+ account: process.env.ACCOUNT2,
|
|
|
+ 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: 0.01,
|
|
|
+ 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() {
|
|
|
+ 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: 10,
|
|
|
+ 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();
|