fidelizare-integrate 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fidelizare-integrate",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Asistent de integrare Fidelizare pentru softuri de gestiune si case de marcat. Scaneaza codul, gaseste locul potrivit si propune integrarea API-ului, in siguranta.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -13,9 +13,8 @@ import { ansi, C, paint, bold, out, sleep } from './ui/ansi.js';
13
13
  import { logoIntro, withSpinner, scanSweep, heading, note, rule, setAnim } from './ui/anim.js';
14
14
  import { detectStack, listSourceFiles } from './core/detect.js';
15
15
  import { scanForIntegrationPoints } from './core/scan.js';
16
- import { proposeChange } from './core/propose.js';
17
16
  import { runAgentLoop } from './core/agent-loop.js';
18
- import { renderPlan, applyPlan, gitState, undoHint } from './core/apply.js';
17
+ import { gitState } from './core/apply.js';
19
18
  import { vaultPut, vaultResolve } from './core/vault.js';
20
19
 
21
20
  const args = process.argv.slice(2);
@@ -67,111 +66,60 @@ async function main() {
67
66
  });
68
67
  note('Limbaj: ' + bold(paint(det.label, C.red)) + paint(' (' + (det.via || '?') + ')', C.dim));
69
68
 
70
- // 4. Scan for integration points
69
+ // 4. Quick scan just a hint; the agent explores the code itself.
71
70
  await scanSweep('Caut unde se citeste codul scanat si se finalizeaza bonul…', 1700);
72
71
  const files = listSourceFiles(root);
73
- const candidates = await withSpinner('Evaluez cele mai sigure locuri de integrare', async () => {
74
- await sleep(400);
75
- return scanForIntegrationPoints(files, root);
76
- });
77
-
78
- if (candidates.length === 0) {
79
- note('Nu am gasit un punct clar de integrare. Ruleaza cu --dir spre folderul softului.', C.amber);
72
+ if (files.length === 0) {
73
+ rule();
74
+ out(' ' + paint('✗ Nu am gasit fisiere sursa in proiect.', C.redBright) + '\n');
75
+ supportNote();
80
76
  rl.close();
81
77
  return;
82
78
  }
83
- const top = candidates[0];
84
- note('Punct gasit: ' + bold(paint(top.rel, C.white)) +
85
- paint(' linia ' + top.topLine.line + ' (' + top.topLine.reasons.join(', ') + ')', C.dim));
79
+ const candidates = scanForIntegrationPoints(files, root);
80
+ if (candidates[0]) {
81
+ note('Punct probabil: ' + bold(paint(candidates[0].rel, C.white)) + paint(' linia ' + candidates[0].topLine.line, C.dim));
82
+ }
86
83
  rule();
87
84
 
88
85
  const git = gitState(root);
89
86
 
90
- // 5a. Agentic tool-loop (real coding agent) the DEFAULT when an LLM key is
91
- // available. The model finds the files and applies the integration itself,
92
- // each write gated. `--no-agent` forces the basic deterministic proposer.
93
- const llmKey = process.env.OPENROUTER_API_KEY || process.env.FIDELIZARE_LLM_KEY;
94
- if (!has('--no-agent') && !llmKey) {
95
- note('Pentru integrare cu AI (recomandat), seteaza OPENROUTER_API_KEY. Acum folosesc modul determinist de baza.', C.amber);
96
- rule();
97
- }
98
- if (!has('--no-agent') && llmKey) {
99
- out(' ' + paint('Agent ', C.ink) + bold(paint(val('--model') || 'Opus 4.8', C.red)) +
100
- paint(' · scrierile cer confirmare (y / a / N)', C.dim) + '\n\n');
101
- const r = await runAgentLoop({
102
- stack: det.stack, rootDir: root, files,
103
- askRaw: ask, auto: AUTO, model: val('--model'),
104
- guideUrl: val('--guide') || process.env.FIDELIZARE_GUIDE_URL,
105
- });
106
- if (r.ok) {
107
- writeEnv(root, vaultResolve(keyRef));
108
- out('\n ' + paint('✓', C.green) + ' ' + paint(r.summary.split('\n')[0].slice(0, 88), C.white) + '\n');
109
- note((r.changes.join(', ') || '—') + paint(' · cheia in .env, anulare: git checkout', C.dim), C.ink);
110
- out('\n ' + paint('Integrare pregatita.', C.green) + ' ' + paint('Fidelizare', C.red) + '\n\n');
111
- rl.close();
112
- return;
113
- }
114
- note('Agent indisponibil (' + r.reason + '). Trec la propunerea determinista.', C.amber);
115
- }
87
+ // 5. Agent the ONLY path. Inference via the Fidelizare gateway (the client
88
+ // uses only their Fidelizare key) or OPENROUTER_API_KEY for our dev. No
89
+ // deterministic fallback: if it can't run, we say so and point to support.
90
+ out(' ' + paint('Agent ', C.ink) + bold(paint(val('--model') || 'Opus 4.8', C.red)) +
91
+ paint(' · scrierile cer confirmare (y / a / N)', C.dim) +
92
+ (git.isRepo && !git.clean ? paint(' · ai modificari necomise', C.amber) : '') + '\n\n');
93
+
94
+ const r = await runAgentLoop({
95
+ stack: det.stack, rootDir: root, files,
96
+ askRaw: ask, auto: AUTO, model: val('--model'),
97
+ guideUrl: val('--guide') || process.env.FIDELIZARE_GUIDE_URL,
98
+ fidelKey: vaultResolve(keyRef),
99
+ });
116
100
 
117
- // 5b. Deterministic proposer (fallback / no key).
118
- heading('Propunere de integrare');
119
- let plan = null;
120
- if (!plan) {
121
- plan = await withSpinner('Construiesc modificarea minima si sigura', async () => {
122
- await sleep(500);
123
- return proposeChange({ stack: det.stack, candidate: top, rootDir: root });
124
- });
125
- }
126
- if (!plan) {
127
- note('Nu pot genera o propunere pentru acest limbaj inca: ' + det.label, C.amber);
101
+ if (r.ok) {
102
+ writeEnv(root, vaultResolve(keyRef));
103
+ out('\n ' + paint('✓', C.green) + ' ' + paint(r.summary.split('\n')[0].slice(0, 88), C.white) + '\n');
104
+ note((r.changes.join(', ') || '—') + paint(' · cheia in .env, anulare: git checkout', C.dim), C.ink);
105
+ out('\n ' + paint('Integrare pregatita.', C.green) + ' ' + paint('Fidelizare', C.red) + '\n\n');
128
106
  rl.close();
129
107
  return;
130
108
  }
131
- note(plan.summary, C.ink);
132
- renderPlan(plan, root);
133
- rule();
134
-
135
- // 6. Safety: git state + confirmation (git computed above)
136
- heading('Siguranta');
137
- if (!git.isRepo) note('• Nu e repo git. Vom face copii .bak pentru fiecare fisier modificat.', C.amber);
138
- else if (!git.clean) note('• Repo git cu modificari necomise. Recomandat: commit inainte (le poti anula cu git checkout).', C.amber);
139
- else note('• Repo git curat. Orice modificare poate fi anulata cu git checkout.', C.green);
140
- note('• Modific doar fisiere din proiect. Cheia API merge in .env, nu in cod.', C.green);
141
-
142
- let go = AUTO;
143
- if (!AUTO) {
144
- const a = (await ask('\n Aplic modificarile? [y/N]')).toLowerCase();
145
- go = a === 'y' || a === 'yes' || a === 'da' || a === 'd';
146
- }
147
- if (!go) { note('Anulat. Nimic nu a fost scris.', C.amber); rl.close(); return; }
148
-
149
- // 7. Apply + write .env (secret resolved host-side from the vault)
150
- const res = await withSpinner('Aplic modificarile si scriu configuratia', async () => {
151
- await sleep(500);
152
- const r = applyPlan(plan, root);
153
- const envPath = join(root, '.env');
154
- const keyVal = vaultResolve(keyRef);
155
- const block = `\n# Fidelizare\nFIDELIZARE_API_KEY=${keyVal}\nFIDELIZARE_STORE_ID=MAGAZINUL-TAU\nFIDELIZARE_POS_ID=CASA-1\n`;
156
- if (!existsSync(envPath) || !readFileSync(envPath, 'utf8').includes('FIDELIZARE_API_KEY')) {
157
- appendFileSync(envPath, block);
158
- }
159
- r.written.push(envPath);
160
- return r;
161
- });
162
109
 
110
+ // No fallback. Clear error + support ticket.
163
111
  rule();
164
- heading('Gata');
165
- note('Fisiere scrise:', C.white);
166
- for (const w of res.written) out(' ' + paint('•', C.green) + ' ' + paint(w.replace(root, '.'), C.ink) + '\n');
167
- out('\n');
168
- undoHint(root, res);
169
- note('Test: ruleaza un cod de fidelitate (ex 12333) prin scanner si verifica punctele in panoul tau.', C.ink);
170
- note('Documentatie completa: cere acces pe fidelizare.ro/integrare', C.dim);
171
- out('\n ' + paint('Integrare pregatita.', C.green) + ' ' + paint('Fidelizare', C.red) + '\n\n');
112
+ out(' ' + paint('✗ Integrarea nu a putut rula.', C.redBright) + ' ' + paint('(' + r.reason + ')', C.dim) + '\n');
113
+ if (r.reason === 'no_key') note('Lipseste cheia. Lipeste cheia ta API Fidelizare cand ti-o cere CLI-ul.', C.amber);
114
+ supportNote();
172
115
  rl.close();
173
116
  }
174
117
 
118
+ function supportNote() {
119
+ out(' ' + paint('Nu merge? Scrie-ne si rezolvam:', C.amber) + ' ' +
120
+ paint('https://fidelizare.ro/integrare#acces', C.white) + paint(' · contact@fidelizare.ro', C.dim) + '\n\n');
121
+ }
122
+
175
123
  // Write the Fidelizare config block to .env (idempotent). The key comes from
176
124
  // the host-side vault, never from a diff or the model.
177
125
  function writeEnv(root, keyVal) {
@@ -15,6 +15,9 @@ import { C, paint, out } from '../ui/ansi.js';
15
15
  import { thinking, toolLine } from '../ui/anim.js';
16
16
 
17
17
  const URL = 'https://openrouter.ai/api/v1/chat/completions';
18
+ // Fidelizare-hosted gateway: clients run the agent with ONLY their Fidelizare
19
+ // key; inference is on us, the OpenRouter key stays server-side.
20
+ const GATEWAY_URL = 'https://mcp.fidelizare.ro/llm';
18
21
  const DEFAULT_MODEL = 'anthropic/claude-opus-4.8';
19
22
  const DEFAULT_GUIDE = 'https://mcp.fidelizare.ro/assets/api-guide.md';
20
23
  const MAX_STEPS = 30;
@@ -53,9 +56,13 @@ const TOOLS = [
53
56
  { type: 'function', function: { name: 'finish', description: 'Termina integrarea.', parameters: { type: 'object', properties: { summary: { type: 'string' } }, required: ['summary'] } } },
54
57
  ];
55
58
 
56
- export async function runAgentLoop({ stack, rootDir, files, askRaw, auto, model, guideUrl }) {
57
- const key = process.env.OPENROUTER_API_KEY || process.env.FIDELIZARE_LLM_KEY;
58
- if (!key) return { ok: false, reason: 'no_key' };
59
+ export async function runAgentLoop({ stack, rootDir, files, askRaw, auto, model, guideUrl, fidelKey }) {
60
+ // Dev path: OPENROUTER_API_KEY hits OpenRouter directly. Otherwise the client
61
+ // uses the Fidelizare gateway, authenticated by their Fidelizare key.
62
+ const orKey = process.env.OPENROUTER_API_KEY || process.env.FIDELIZARE_LLM_KEY;
63
+ const endpoint = orKey ? URL : (process.env.FIDELIZARE_GATEWAY_URL || GATEWAY_URL);
64
+ const authKey = orKey || fidelKey;
65
+ if (!authKey) return { ok: false, reason: 'no_key' };
59
66
  const usedModel = model || DEFAULT_MODEL;
60
67
  const guide = guideUrl || DEFAULT_GUIDE;
61
68
 
@@ -154,9 +161,9 @@ export async function runAgentLoop({ stack, rootDir, files, askRaw, auto, model,
154
161
  const stop = thinking('Gandeste');
155
162
  let data;
156
163
  try {
157
- const res = await fetch(URL, {
164
+ const res = await fetch(endpoint, {
158
165
  method: 'POST',
159
- headers: { Authorization: 'Bearer ' + key, 'Content-Type': 'application/json', 'X-Title': 'Fidelizare Integrate' },
166
+ headers: { Authorization: 'Bearer ' + authKey, 'Content-Type': 'application/json', 'X-Title': 'Fidelizare Integrate' },
160
167
  body: JSON.stringify({ model: usedModel, temperature: 0, tools: TOOLS, messages }),
161
168
  signal: AbortSignal.timeout(90_000),
162
169
  });
@@ -1,91 +0,0 @@
1
- // Deterministic proposal: given the detected stack and the best candidate
2
- // location, build a safe change plan (a new helper file + the inserted call +
3
- // an import). Language-aware indentation and import anchoring so the result
4
- // stays syntactically valid. Runs without an LLM; the agent mode (agent-loop)
5
- // replaces it for arbitrary real codebases.
6
- import { readFileSync } from 'fs';
7
- import { dirname, join } from 'path';
8
- import { HELPERS } from './snippets.js';
9
-
10
- export function proposeChange({ stack, candidate, rootDir }) {
11
- const helper = HELPERS[stack];
12
- if (!helper) return null;
13
-
14
- const plan = { stack, newFiles: [], edits: [], summary: '' };
15
- const targetDir = candidate ? dirname(candidate.file) : rootDir;
16
- if (helper.content) {
17
- plan.newFiles.push({ path: join(targetDir, helper.filename), content: helper.content });
18
- }
19
-
20
- if (candidate) {
21
- const lines = readFileSync(candidate.file, 'utf8').split('\n');
22
- const at = findFidelityLine(lines) ?? candidate.topLine.line;
23
- const bodyIndent = blockBodyIndent(lines, at);
24
- const codVar = inferCodeVar(lines[at - 1], stack);
25
- const cmt = stack === 'python' ? '#' : '//';
26
-
27
- plan.edits.push({
28
- path: candidate.file,
29
- atLine: at,
30
- insert: [
31
- bodyIndent + cmt + ' Fidelizare: cod de fidelitate detectat -> acumuleaza puncte',
32
- bodyIndent + helper.call(codVar),
33
- ],
34
- reason: 'Apel API la detectarea cardului de fidelitate',
35
- });
36
-
37
- if (helper.importLine) {
38
- const anchor = importAnchor(lines, stack);
39
- plan.edits.push({ path: candidate.file, atLine: anchor, insert: [helper.importLine], reason: 'Import helper Fidelizare' });
40
- }
41
- }
42
-
43
- plan.summary = `${plan.newFiles.length} fisier nou + ${plan.edits.length} modificare in ` +
44
- (candidate ? candidate.rel : 'proiect');
45
- return plan;
46
- }
47
-
48
- // The safest place to call the API is exactly where the code detects a
49
- // fidelity card (a branch on it).
50
- function findFidelityLine(lines) {
51
- const FID = /(fidelit|fideliz|loyalty|loyaltycard|cardclient)/i;
52
- const BRANCH = /\b(if|elif|else|when|case|switch)\b/i;
53
- for (let i = 0; i < lines.length; i++) {
54
- if (FID.test(lines[i]) && BRANCH.test(lines[i]) && !/^\s*(\/\/|#|\*)/.test(lines[i])) return i + 1;
55
- }
56
- for (let i = 0; i < lines.length; i++) {
57
- if (FID.test(lines[i]) && !/^\s*(\/\/|#|\*)/.test(lines[i])) return i + 1;
58
- }
59
- return null;
60
- }
61
-
62
- // Match the indentation of the block opened at `at` by looking at the next
63
- // non-empty line that is more indented; else the line indent + one step.
64
- function blockBodyIndent(lines, at) {
65
- const own = lines[at - 1].match(/^\s*/)?.[0] || '';
66
- for (let i = at; i < lines.length; i++) {
67
- if (!lines[i].trim()) continue;
68
- const ind = lines[i].match(/^\s*/)?.[0] || '';
69
- if (ind.length > own.length) return ind;
70
- break;
71
- }
72
- return own + (own.includes('\t') ? '\t' : ' ');
73
- }
74
-
75
- function inferCodeVar(line, stack) {
76
- const matches = [...line.matchAll(/\(\s*(\$?[a-zA-Z_][\w$]*)\s*\)/g)];
77
- if (matches.length) return matches[matches.length - 1][1];
78
- return stack === 'php' ? '$code' : 'code';
79
- }
80
-
81
- function importAnchor(lines, stack) {
82
- const IMP = /^\s*(import |from .+ import|require_once|require|use |#include|uses )/i;
83
- let last = 0;
84
- lines.forEach((l, i) => { if (IMP.test(l)) last = i + 1; });
85
- if (last > 0) return last;
86
- if (stack === 'php') {
87
- const tag = lines.findIndex((l) => /<\?php/.test(l));
88
- return tag >= 0 ? tag + 1 : 0;
89
- }
90
- return 0;
91
- }
@@ -1,187 +0,0 @@
1
- // Canonical integration artifacts per stack: a helper file that wraps the
2
- // Fidelizare API call, and the call inserted where the POS detects a fidelity
3
- // card. The API key is referenced from the environment, never inlined.
4
-
5
- export const HELPERS = {
6
- node: {
7
- filename: 'fidelizare.js',
8
- importLine: `import { acumuleazaPuncte } from "./fidelizare.js";`,
9
- call: (v) => `await acumuleazaPuncte(${v}, cart, total);`,
10
- content: `// Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
11
- const FIDELIZARE_API = "https://fidelizare.ro/api/integration/s2s/submitReceipt";
12
-
13
- export async function acumuleazaPuncte(codFidelitate, items, total) {
14
- try {
15
- const bon = "BF" + Date.now();
16
- const res = await fetch(FIDELIZARE_API, {
17
- method: "POST",
18
- headers: {
19
- Authorization: "Bearer " + process.env.FIDELIZARE_API_KEY,
20
- "Content-Type": "application/json",
21
- "Idempotency-Key": "BON-" + bon,
22
- },
23
- body: JSON.stringify({
24
- store_id: process.env.FIDELIZARE_STORE_ID || "MAGAZINUL-TAU",
25
- pos_id: process.env.FIDELIZARE_POS_ID || "CASA-1",
26
- receipt_number: bon,
27
- fidelity_card: codFidelitate,
28
- total,
29
- items,
30
- }),
31
- });
32
- return await res.json();
33
- } catch (e) {
34
- return { success: false }; // nu blocheaza vanzarea
35
- }
36
- }
37
- `,
38
- },
39
-
40
- php: {
41
- filename: 'Fidelizare.php',
42
- importLine: `require_once __DIR__ . '/Fidelizare.php';`,
43
- call: (v) => `Fidelizare::acumuleazaPuncte(${v}, $cart, $total);`,
44
- content: `<?php
45
- // Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
46
- class Fidelizare {
47
- public static function acumuleazaPuncte($codFidelitate, $items, $total) {
48
- try {
49
- $bon = 'BF' . time();
50
- $ch = curl_init('https://fidelizare.ro/api/integration/s2s/submitReceipt');
51
- curl_setopt_array($ch, [
52
- CURLOPT_POST => true,
53
- CURLOPT_RETURNTRANSFER => true,
54
- CURLOPT_TIMEOUT => 3,
55
- CURLOPT_HTTPHEADER => [
56
- 'Authorization: Bearer ' . getenv('FIDELIZARE_API_KEY'),
57
- 'Content-Type: application/json',
58
- 'Idempotency-Key: BON-' . $bon,
59
- ],
60
- CURLOPT_POSTFIELDS => json_encode([
61
- 'store_id' => getenv('FIDELIZARE_STORE_ID') ?: 'MAGAZINUL-TAU',
62
- 'pos_id' => getenv('FIDELIZARE_POS_ID') ?: 'CASA-1',
63
- 'receipt_number' => $bon,
64
- 'fidelity_card' => $codFidelitate,
65
- 'total' => $total,
66
- 'items' => $items,
67
- ]),
68
- ]);
69
- $res = curl_exec($ch);
70
- curl_close($ch);
71
- return json_decode($res, true);
72
- } catch (\\Throwable $e) {
73
- return ['success' => false]; // nu blocheaza vanzarea
74
- }
75
- }
76
- }
77
- `,
78
- },
79
-
80
- python: {
81
- filename: 'fidelizare.py',
82
- importLine: `from fidelizare import acumuleaza_puncte`,
83
- call: (v) => `acumuleaza_puncte(${v}, cart, total)`,
84
- content: `# Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
85
- import os, time, json, urllib.request
86
-
87
-
88
- def acumuleaza_puncte(cod_fidelitate, items, total):
89
- try:
90
- bon = "BF" + str(int(time.time()))
91
- body = json.dumps({
92
- "store_id": os.environ.get("FIDELIZARE_STORE_ID", "MAGAZINUL-TAU"),
93
- "pos_id": os.environ.get("FIDELIZARE_POS_ID", "CASA-1"),
94
- "receipt_number": bon,
95
- "fidelity_card": cod_fidelitate,
96
- "total": total,
97
- "items": items,
98
- }).encode()
99
- req = urllib.request.Request(
100
- "https://fidelizare.ro/api/integration/s2s/submitReceipt",
101
- data=body, method="POST",
102
- headers={
103
- "Authorization": "Bearer " + os.environ.get("FIDELIZARE_API_KEY", ""),
104
- "Content-Type": "application/json",
105
- "Idempotency-Key": "BON-" + bon,
106
- })
107
- with urllib.request.urlopen(req, timeout=3) as r:
108
- return json.loads(r.read())
109
- except Exception:
110
- return {"success": False} # nu blocheaza vanzarea
111
- `,
112
- },
113
-
114
- csharp: {
115
- filename: 'Fidelizare.cs',
116
- call: (v) => `await Fidelizare.AcumuleazaPuncte(${v});`,
117
- content: `// Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
118
- using System;
119
- using System.Net.Http;
120
- using System.Text;
121
- using System.Threading.Tasks;
122
-
123
- public static class Fidelizare {
124
- static readonly HttpClient Http = new HttpClient();
125
- public static async Task AcumuleazaPuncte(string codFidelitate) {
126
- try {
127
- var bon = "BF" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
128
- var json = "{\\"fidelity_card\\":\\"" + codFidelitate + "\\"}";
129
- var req = new HttpRequestMessage(HttpMethod.Post,
130
- "https://fidelizare.ro/api/integration/s2s/submitReceipt");
131
- req.Headers.Add("Authorization", "Bearer " + Environment.GetEnvironmentVariable("FIDELIZARE_API_KEY"));
132
- req.Headers.Add("Idempotency-Key", "BON-" + bon);
133
- req.Content = new StringContent(json, Encoding.UTF8, "application/json");
134
- await Http.SendAsync(req);
135
- } catch { /* nu blocheaza vanzarea */ }
136
- }
137
- }
138
- `,
139
- },
140
-
141
- java: {
142
- filename: 'Fidelizare.java',
143
- call: (v) => `Fidelizare.acumuleazaPuncte(${v});`,
144
- content: `// Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
145
- import java.net.URI;
146
- import java.net.http.*;
147
-
148
- public class Fidelizare {
149
- public static void acumuleazaPuncte(String codFidelitate) {
150
- try {
151
- String body = "{\\"fidelity_card\\":\\"" + codFidelitate + "\\"}";
152
- HttpRequest req = HttpRequest.newBuilder()
153
- .uri(URI.create("https://fidelizare.ro/api/integration/s2s/submitReceipt"))
154
- .header("Authorization", "Bearer " + System.getenv("FIDELIZARE_API_KEY"))
155
- .POST(HttpRequest.BodyPublishers.ofString(body)).build();
156
- HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
157
- } catch (Exception e) { /* nu blocheaza vanzarea */ }
158
- }
159
- }
160
- `,
161
- },
162
-
163
- delphi: {
164
- filename: 'Fidelizare.pas',
165
- call: (v) => `AcumuleazaPuncte(${v});`,
166
- content: `// Generat de fidelizare-integrate. Cheia din mediu, niciodata in cod.
167
- unit Fidelizare;
168
- interface
169
- procedure AcumuleazaPuncte(const CodFidelitate: string);
170
- implementation
171
- uses System.Net.HttpClient, System.Classes, System.SysUtils;
172
- procedure AcumuleazaPuncte(const CodFidelitate: string);
173
- var Http: THTTPClient; Body: TStringStream;
174
- begin
175
- try
176
- Http := THTTPClient.Create;
177
- Body := TStringStream.Create('{"fidelity_card":"' + CodFidelitate + '"}', TEncoding.UTF8);
178
- Http.CustomHeaders['Authorization'] := 'Bearer ' + GetEnvironmentVariable('FIDELIZARE_API_KEY');
179
- Http.Post('https://fidelizare.ro/api/integration/s2s/submitReceipt', Body);
180
- except
181
- // nu blocheaza vanzarea
182
- end;
183
- end;
184
- end.
185
- `,
186
- },
187
- };