genial-cli 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. #!/usr/bin/env -S /usr/bin/node
  2. require("dotenv").config({ quiet: true });
  3. const decimal = require("decimal.js");
  4. const randomUUID = require("crypto").randomUUID;
  5. const DEBUG = true;
  6. const AUTH_URL =
  7. "https://genial-arquitetura-authentication.homolog.api.genial.systems";
  8. const COREBK_URL =
  9. "https://genial-arquitetura-corebank.homolog.api.genial.systems";
  10. const BAAS_URL = "https://gerador-arquitetura-baas.homolog.api.genial.systems";
  11. const OPS_URL = "https://genial-baas-operations.homolog.api.genial.systems";
  12. async function main() {
  13. const args = process.argv.slice(2);
  14. if (args.length === 0) return help();
  15. const cmd = args[0];
  16. const rest = args.slice(1);
  17. try {
  18. switch (cmd) {
  19. case "token": {
  20. const [ok, tok] = await getToken();
  21. if (!ok) return fail(tok);
  22. console.log(tok);
  23. break;
  24. }
  25. case "balance": {
  26. await Balance(rest[0]);
  27. break;
  28. }
  29. case "getkey": {
  30. if (rest.length < 1) return fail("Usage: getkey <pixKey>");
  31. await getKey(rest[0]);
  32. break;
  33. }
  34. case "decode": {
  35. if (rest.length < 1) return fail("Usage: decodeQrcode <qrcode>");
  36. await decodeQrcode(rest[0]);
  37. break;
  38. }
  39. case "payQr": {
  40. if (rest.length < 2)
  41. return fail("Usage: payQr <qrCodeCopyPaste> <value>");
  42. console.log(rest[0], rest[1]);
  43. await PayQr(rest[0], rest[1]);
  44. break;
  45. }
  46. case "qrcodeStatic": {
  47. await qrcodeStatic();
  48. break;
  49. }
  50. case "paykey": {
  51. if (rest.length < 2) return fail("Usage: payInit <pixKey> <value>");
  52. await PayInit(rest[0], rest[1]);
  53. break;
  54. }
  55. case "qrcodedynamic": {
  56. if (rest.length < 1) return fail("Usage: qrcode <value>");
  57. await QrCodeDynamic(rest[0]);
  58. break;
  59. }
  60. case "statusIn": {
  61. //not working on my machine
  62. if (rest.length < 1) return fail("Usage: statusIn <transactionId>");
  63. await StatusIn(rest[0]);
  64. break;
  65. }
  66. case "statusOut": {
  67. //not working on my machine
  68. if (rest.length < 1) return fail("Usage: statusOut <e2e>");
  69. await StatusOut(rest[0]);
  70. break;
  71. }
  72. case "refund": {
  73. if (rest.length < 1) return fail("Usage: refund <eventId> <value>");
  74. await Refund(rest[0], rest[1]);
  75. break;
  76. }
  77. default:
  78. help();
  79. return;
  80. }
  81. } catch (e) {
  82. fail(e.message || e);
  83. help();
  84. return;
  85. }
  86. }
  87. function help() {
  88. console.log(`Usage: genial-cli <command> [args]
  89. Commands:
  90. token
  91. balance <accountNumber>
  92. getkey <accountNumber> <pixKey>
  93. statusOut <instantPaymentId> <YYYY-MM-DD>
  94. statusIn <transactionId>
  95. qrcode <accountNumber> <value>
  96. payInit [value] [pixKey]
  97. pay <idempotencyId>
  98. payQr <accountNumber> <value> <qrCodeCopyPaste>
  99. refund <eventId> <value>
  100. preview <emv>
  101. boletoPay <taxId> <line> [description] [uuid]
  102. boletoGet <id>
  103. `);
  104. process.exit(1);
  105. }
  106. function fail(msg) {
  107. console.error("Error:", msg);
  108. process.exit(1);
  109. }
  110. /* =============== Auth =============== */
  111. async function getToken() {
  112. const url = `${AUTH_URL}/v1/authentication`;
  113. const options = {
  114. method: "POST",
  115. headers: {
  116. basic: process.env.TOKEN_BASIC,
  117. "X-ORIGEM": process.env.TOKEN_XORIGEM,
  118. },
  119. };
  120. try {
  121. if (DEBUG) {
  122. console.log("\nDEBUG callApi:", options.method, url);
  123. }
  124. const res = await fetch(url, options);
  125. const text = await res.text();
  126. if (DEBUG) {
  127. console.log("DEBUG getToken status:", res.status);
  128. console.log("DEBUG getToken body:", text);
  129. }
  130. if (res.status < 200 || res.status >= 300)
  131. return [false, { status: res.status, body: text }];
  132. const json = JSON.parse(text);
  133. if (!json.token) return [false, "No token in response"];
  134. return [true, json.token];
  135. } catch (e) {
  136. return [false, e.message];
  137. }
  138. }
  139. async function callApi(method, url, body) {
  140. const [ok, token] = await getToken();
  141. if (!ok) return [false, token];
  142. const headers = {
  143. Authorization: token,
  144. "X-ORIGEM": process.env.TOKEN_XORIGEM,
  145. };
  146. const options = { method, headers };
  147. if (method.toUpperCase() === "POST") {
  148. headers["Content-Type"] = "application/json";
  149. options.body = body ? JSON.stringify(body) : "";
  150. }
  151. if (DEBUG) {
  152. console.log("\nDEBUG callApi:", method, url);
  153. if (body) console.log("DEBUG body:", JSON.stringify(body));
  154. }
  155. try {
  156. const res = await fetch(url, options);
  157. const text = await res.text();
  158. if (DEBUG) {
  159. console.log("DEBUG status:", res.status);
  160. console.log("DEBUG raw:", text);
  161. }
  162. if (res.status < 200 || res.status >= 300)
  163. return [false, { status: res.status, body: text }];
  164. try {
  165. return [true, JSON.parse(text)];
  166. } catch {
  167. return [true, text];
  168. }
  169. } catch (e) {
  170. return [false, e.message];
  171. }
  172. }
  173. /**
  174. * @description
  175. * account 2 is me the user
  176. * account 1 is smartpay the receiver of the payment
  177. */
  178. /* =============== 1) Balance =============== done*/
  179. async function Balance(accountNumber) {
  180. //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
  181. let account = process.env.ACCOUNT1;
  182. const url = `${COREBK_URL}/v1/core-banking/balance/1/${account}`;
  183. const [ok, out] = await callApi("GET", url);
  184. if (!ok) return fail(out);
  185. console.dir(out, { depth: null });
  186. }
  187. /* =============== 2) Dict Key =============== done*/
  188. async function getKey(pixKey) {
  189. const url = `${BAAS_URL}/v2/pix/key-account-partner/1/${process.env.ACCOUNT1}/${pixKey}`;
  190. const [ok, out] = await callApi("GET", url);
  191. if (!ok) return fail(out);
  192. console.dir(out, { depth: null });
  193. }
  194. /* =============== DECODE QRCODE =============== done*/
  195. async function decodeQrcode(qrcode) {
  196. const url = `https://gerador-arquitetura-baas.homolog.api.genial.systems/v1/pix/qrcode-decode?qrcode=${encodeURIComponent(
  197. qrcode
  198. )}`;
  199. const [ok, out] = await callApi("GET", url);
  200. if (!ok) return fail(out);
  201. console.dir(out, { depth: null });
  202. }
  203. /* =============== 8) Pay by QR =============== done*/
  204. async function PayQr(qrCodeCopyPaste, value) {
  205. const url = `${BAAS_URL}/v1/pix/copy-paste`;
  206. /**
  207. * debit is our account that is going to pay the qr code, need prod keys to do it correctly
  208. */
  209. const body = {
  210. qrCodeCopyPaste,
  211. value: Number(value),
  212. debit: {
  213. name: process.env.ACCOUNT2_NAME,
  214. agency: 1,
  215. account: process.env.ACCOUNT2,
  216. accountType: "CACC",
  217. },
  218. };
  219. console.log(body);
  220. const [ok, out] = await callApi("POST", url, body);
  221. if (!ok) return fail(out);
  222. console.dir(out, { depth: null });
  223. }
  224. /* =============== 2) QR Code Static =============== done*/
  225. async function qrcodeStatic() {
  226. const url = `${BAAS_URL}/v1/pix/qrcode-static/`;
  227. const body = {
  228. accountHolderName: process.env.ACCOUNT1_NAME,
  229. addressingKey: {
  230. key: process.env.ACCOUNT1_KEY,
  231. type: "EVP",
  232. },
  233. account: process.env.ACCOUNT1,
  234. agency: 1,
  235. // additionalInformation: "Pagamento de teste genial-cli" /**@optional */,
  236. // value: 10.0, /**@optional */
  237. };
  238. const [ok, out] = await callApi("POST", url, body);
  239. if (!ok) return fail(out);
  240. console.dir(out, { depth: null });
  241. }
  242. /* =============== 3) Status OUT (PAC008) =============== NEED TO CHECK*/
  243. async function StatusOut(e2e) {
  244. const url = `${OPS_URL}/v1/cashout/${encodeURIComponent(e2e)}`;
  245. const [ok, out] = await callApi("GET", url);
  246. if (!ok) return fail(out);
  247. console.dir(out, { depth: null });
  248. }
  249. /* =============== 4) Status IN (PAC002) =============== NEED TO CHECK*/
  250. async function StatusIn(transactionId) {
  251. const url = `${OPS_URL}/v1/cashin/end-to-end-id/${encodeURIComponent(
  252. transactionId
  253. )}`;
  254. const [ok, out] = await callApi("GET", url);
  255. if (!ok) return fail(out);
  256. console.dir(out, { depth: null });
  257. }
  258. /* =============== 6) Init Pix by Key (PAC008 Start) =============== done */
  259. /**
  260. *
  261. * @param pixKey
  262. * @param value
  263. * @returns confirmed idempotencyId [@description we need to create the idempotencyId on our side to confirm the payment later ]
  264. */
  265. async function PayInit(pixKey, value) {
  266. const url = `${BAAS_URL}/v1/pix/send/initialization`;
  267. const body = {
  268. debit: {
  269. name: process.env.ACCOUNT1_NAME,
  270. agency: 1,
  271. account: process.env.ACCOUNT1,
  272. accountType: "CACC",
  273. },
  274. credit: {
  275. key: pixKey,
  276. },
  277. value: String(value),
  278. idempotencyId: randomUUID(),
  279. };
  280. var [ok, out] = await callApi("POST", url, body);
  281. if (!ok) return fail(out);
  282. console.dir(out, { depth: null });
  283. if (out?.data?.idempotencyId) {
  284. console.log("idempotencyId:", out.data.idempotencyId);
  285. }
  286. var [ok, reply] = await paykey(out.data.idempotencyId);
  287. if (!ok) return fail(reply);
  288. console.dir(reply, { depth: null });
  289. }
  290. /* =============== 7) Pay by Idempotency (PAC008) =============== done */
  291. /**
  292. *
  293. * @param idempotencyId
  294. * @param payinfo
  295. * @returns
  296. */
  297. async function paykey(idempotencyId, payinfo) {
  298. const url = `${BAAS_URL}/v1/pix/send/confirmed`;
  299. const body = {
  300. idempotencyId: idempotencyId,
  301. ignorePaymentDuplication: false,
  302. // additionalInformation: payinfo, /**@optional */
  303. };
  304. const [ok, out] = await callApi("POST", url, body);
  305. if (!ok) return fail(out);
  306. if (DEBUG) console.log("Payment confirmed for idempotencyId:", idempotencyId);
  307. return [true, out];
  308. }
  309. /* =============== 9) Refund (PAC004) =============== worked*/
  310. async function Refund(eventId, value) {
  311. const url = `${BAAS_URL}/v1/pix/return`;
  312. const body = {
  313. value: value,
  314. returnReasonCode: "MD06",
  315. eventId: eventId,
  316. };
  317. const [ok, out] = await callApi("POST", url, body);
  318. if (!ok) return fail(out);
  319. console.dir(out, { depth: null });
  320. }
  321. async function QrCodeDynamic(value) {
  322. const url = `${BAAS_URL}/v2/qrcode/dynamic?agency=1&account=${process.env.ACCOUNT1}`;
  323. const body = {
  324. items: [
  325. {
  326. addressingKey: {
  327. key: process.env.ACCOUNT1_KEY,
  328. type: "EVP",
  329. },
  330. accountHolderName: process.env.ACCOUNT1_NAME,
  331. accountHolderCity: "SAO PAULO",
  332. dynamicQRCodeType: "IMMEDIATE",
  333. immediate: {
  334. expiration: 36000,
  335. paymentValue: {
  336. documentValue: value,
  337. showPaymentValueInQrCode: true,
  338. //"cashback": {
  339. //"cashbackType": "WITHDRAW",
  340. //"value": 0,
  341. //"allowValueChange": true,
  342. //"withdrawProviders": {
  343. //"agentModality": "string",
  344. //"serviceProvider": "string"
  345. //}
  346. //}
  347. },
  348. payerInformation: {
  349. cpfCnpj: "37812511871",
  350. name: "gustavo lopes",
  351. validatePayerInformation: true,
  352. },
  353. receiverInformation: {
  354. taxId: process.env.DOCUMENT_NUMBER,
  355. name: process.env.ACCOUNT1_NAME,
  356. tradeName: process.env.ACCOUNT1_NAME,
  357. },
  358. },
  359. payerRequestInformation: "TESTE SOLICITAÇÃO AO PAYER",
  360. //"additionalInformation": [
  361. //{
  362. //"name": "string",
  363. //"content": "string",
  364. //"showToPayer": true
  365. //}
  366. //],
  367. //"itemId": "string",
  368. //"receiverTxId": "CTXXXXXXXXXXXX999999995800"
  369. },
  370. ],
  371. };
  372. const [ok, out] = await callApi("POST", url, body);
  373. if (!ok) return fail(out);
  374. console.dir(out, { depth: null });
  375. }
  376. /* =============== 10) Preview Pagamento (BOLETO) =============== did not test*/
  377. async function Preview(emv) {
  378. const url = `${BAAS_URL}/v1/payments/preview`;
  379. const body = { emv };
  380. const [ok, out] = await callApi("POST", url, body);
  381. if (!ok) return fail(out);
  382. console.dir(out, { depth: null });
  383. }
  384. /* =============== 11) Boleto Pay =============== did not test*/
  385. async function BoletoPay(taxId, line, description, uuid) {
  386. const url = `${BAAS_URL}/v1/payments/boleto`;
  387. const body = {
  388. taxId,
  389. description: description || "Pagamento de boleto",
  390. line,
  391. uuid: uuid || `BOL-${Date.now()}`,
  392. };
  393. const [ok, out] = await callApi("POST", url, body);
  394. if (!ok) return fail(out);
  395. console.dir(out, { depth: null });
  396. }
  397. /* =============== 12) Boleto Get by Id =============== did not test*/
  398. async function BoletoGet(id) {
  399. const url = `${BAAS_URL}/v1/payments/boleto/${encodeURIComponent(id)}`;
  400. const [ok, out] = await callApi("GET", url);
  401. if (!ok) return fail(out);
  402. console.dir(out, { depth: null });
  403. }
  404. main();