parche-cli 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joaquín González
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,96 @@
1
- # Temporary Holding Version
1
+ # Parche — Dependabot para APIs
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![CI](https://github.com/Pepo08/parche/actions/workflows/ci.yml/badge.svg)](https://github.com/Pepo08/parche/actions/workflows/ci.yml)
4
+
5
+ Parche lee tu código, arma un inventario de qué APIs de terceros usás (Mercado Pago, ARCA, modelos de IA…) y lo cruza contra una base de **cambios de API verificados**. Cuando un proveedor cambia algo que te afecta, Parche abre un PR con el arreglo, explica qué cambió y por qué, y marca lo que necesita una persona. Ningún PR sale sin pasar la verificación.
6
+
7
+ ```
8
+ $ parche scan .
9
+ APIs encontradas:
10
+ mercadopago 4 usos en 4 archivo(s) [Payment.create, Payment.get]
11
+ arca 3 usos en 1 archivo(s) [ElectronicBilling.createNextVoucher]
12
+ ia 2 usos en 1 archivo(s) [modelo claude-3-5-haiku-20241022, OpenAI Assistants API]
13
+
14
+ ■ Condición frente al IVA del receptor obligatoria en WSFEv1
15
+ ARCA (ex AFIP) · severidad critical · vence 2026-12-01 (47 días)
16
+ ✅ automático src/facturacion.js:14 Agregado CondicionIVAReceptorId: 5 (Consumidor Final)…
17
+ 👀 revisar src/facturacion.js:56 Agregado CondicionIVAReceptorId: 5 por inferencia (DNI + comprobante B/C)…
18
+ ✋ falta un dato src/facturacion.js:62 Falta CondicionIVAReceptorId y no se puede inferir… (comprobante clase A).
19
+ …
20
+ ✅ automático: 5 👀 revisar: 8 ✋ falta un dato: 1 ⛔ bloqueado: 1 🔎 detectado: 2
21
+ ```
22
+
23
+ ## Por qué packs y no "un LLM que arregla todo"
24
+
25
+ Ya hay varios prototipos que le piden a un modelo que lea un changelog y escriba el fix. El problema es que el modelo solo sabe lo que aprendió en su entrenamiento, y una migración de pagos o de facturación no admite "casi bien". Parche separa las dos cosas:
26
+
27
+ - **El pack es dato, no prompt.** Cada cambio de API es un JSON versionado con fuente, fecha límite, reglas de detección, mapeo de campos, casos bloqueados y checklist de revisión (ver `packs/`). Un LLM puede ayudar a *redactar* un pack a partir de un changelog, pero una persona lo verifica antes de publicarlo.
28
+ - **El arreglo es determinístico.** Los codemods trabajan sobre el AST, respetan el formato del cliente (el diff de ARCA es de 2 líneas por factura) y son idempotentes.
29
+ - **Lo que no se puede inferir, no se inventa.** Si falta un dato del negocio (¿este cliente con CUIT es Responsable Inscripto o Monotributo?), el PR lo dice y muestra los valores válidos.
30
+ - **Cada PR mergeado, editado o rechazado vuelve como señal** (`.parche/ledger.jsonl`, `parche feedback`). Eso es lo que hace que la base mejore con el uso.
31
+
32
+ ## Uso
33
+
34
+ Requiere Node 22 o más nuevo. El paquete de npm se llama `parche-cli`; si lo instalás (`npm i -g parche-cli`), el comando es `parche`.
35
+
36
+ ```bash
37
+ npx parche-cli packs # cambios que Parche conoce
38
+ npx parche-cli scan . # inventario + qué te afecta (.parche/report.md)
39
+ npx parche-cli fix . # aplica los arreglos en tu working copy
40
+ npx parche-cli pr . --verify # una rama + commit + PR por cambio
41
+ npx parche-cli feedback . --pack <id> --outcome merged|edited|rejected
42
+ ```
43
+
44
+ `parche pr` abre el PR por la API de GitHub si hay `GITHUB_TOKEN` y un remote `origin` en github.com; si no (o con `--dry-run`), deja la rama, el commit y el cuerpo del PR en `.parche/pr-<pack>.md`. Si hay ítems para revisar, el PR se abre como borrador.
45
+
46
+ ### En GitHub Actions
47
+
48
+ Para que corra todas las semanas, copiá [`templates/github-workflow.yml`](templates/github-workflow.yml) a `.github/workflows/parche.yml`. El paso clave es:
49
+
50
+ ```yaml
51
+ - uses: Pepo08/parche@v0
52
+ with:
53
+ verify: 'true' # corre tu npm test antes de abrir cada PR
54
+ # pack: arca.wsfe.condicion-iva-receptor # opcional: un solo pack
55
+ # dry-run: 'true' # opcional: armar las ramas sin abrir PRs
56
+ ```
57
+
58
+ El workflow necesita `permissions: contents: write` y `pull-requests: write`.
59
+
60
+ ### Desarrollo
61
+
62
+ ```bash
63
+ npm install
64
+ npm test # 13 tests; el CI los corre en Linux y Windows
65
+ bash demo.sh # demo completa sobre examples/tienda-demo
66
+ ```
67
+
68
+ ## Estados de cada hallazgo
69
+
70
+ | Estado | Qué significa |
71
+ |---|---|
72
+ | ✅ automático | El arreglo es seguro y queda aplicado. |
73
+ | 👀 revisar | Se aplicó, pero hay una decisión que confirmar (va en el PR, que sale como borrador). |
74
+ | ✋ falta un dato | No se puede arreglar sin información del negocio. El PR dice qué agregar y con qué valores. |
75
+ | ⛔ bloqueado | Migrarlo rompería algo (ej. `application_fee` en Orders). No se toca. |
76
+ | 🔎 detectado | Solo aviso: cambiarlo cambia el comportamiento (ej. modelo de IA retirado). |
77
+
78
+ ## Packs incluidos
79
+
80
+ | Pack | Proveedor | Tipo | Fecha límite | Arreglo |
81
+ |---|---|---|---|---|
82
+ | `arca.wsfe.condicion-iva-receptor` | ARCA | rompe | 2026-12-01 | sí (consumidor final y DNI B/C) |
83
+ | `mercadopago.payments-to-orders` | Mercado Pago | migración | — | sí (creación de pagos en SDK Node v2) |
84
+ | `ai.model-retirements` | Anthropic, OpenAI | retiro | varias | solo detección |
85
+
86
+ ## Verificación
87
+
88
+ Antes de commitear, `--verify` parsea cada archivo modificado (JS/TS) y corre el `npm test` del repo. Si algo falla, no hay PR. El commit lleva la versión del pack que lo generó, para poder rastrear qué regla produjo cada cambio.
89
+
90
+ ## Límites del MVP
91
+
92
+ - Solo Node.js (JS/TS, ESM y CommonJS). Siguen PHP (la mayoría de los sistemas de facturación locales), Python y Java.
93
+ - ARCA: cubre `@afipsdk/afip.js`. Las integraciones SOAP propias se detectan pero no se reescriben.
94
+ - Mercado Pago: migra la creación de pagos con el SDK v2. Las consultas, capturas y reembolsos de pagos existentes se dejan a propósito en Payments API, como indica la guía oficial.
95
+ - Se distribuye como CLI (`npx parche-cli`) + GitHub Action (`Pepo08/parche@v0`). La GitHub App (instalación en un click, PRs automáticos al publicarse un pack nuevo) es el próximo paso.
96
+ - La capa de LLM (redactar packs desde changelogs y resolver casos "falta un dato" leyendo el modelo de datos del cliente) todavía no está incluida; cuando entre, va a pasar por la misma verificación.
package/bin/parche.js ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+ // Parche — Dependabot para APIs.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { run } from '../src/engine.js';
6
+ import { loadPacks } from '../src/packs.js';
7
+ import { scanReport, prBody, counts, LABEL, daysUntil } from '../src/report.js';
8
+ import { verify } from '../src/verify.js';
9
+ import * as git from '../src/git.js';
10
+ import { parseGithubRemote, openPullRequest } from '../src/github.js';
11
+ import { record } from '../src/ledger.js';
12
+
13
+ const HELP = `parche — Dependabot para APIs
14
+
15
+ Uso:
16
+ parche packs Lista los cambios de API que Parche conoce
17
+ parche scan <dir> [--json] Inventario de APIs + qué te afecta
18
+ parche fix <dir> [--pack <id>] Aplica los arreglos en el código
19
+ parche pr <dir> [--pack <id>] [--dry-run] [--verify]
20
+ Rama + commit + PR por cada cambio
21
+ parche feedback <dir> --pack <id> --outcome merged|edited|rejected [--note "..."]
22
+ `;
23
+
24
+ function parseArgs(argv) {
25
+ const args = { _: [] };
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const a = argv[i];
28
+ if (a.startsWith('--')) {
29
+ const key = a.slice(2);
30
+ const next = argv[i + 1];
31
+ if (next !== undefined && !next.startsWith('--')) { args[key] = next; i++; } else args[key] = true;
32
+ } else args._.push(a);
33
+ }
34
+ return args;
35
+ }
36
+
37
+ function summary(findings) {
38
+ const c = counts(findings);
39
+ return ['auto', 'needs-review', 'needs-input', 'blocked', 'detect-only']
40
+ .filter((k) => c[k]).map((k) => `${LABEL[k]}: ${c[k]}`).join(' ');
41
+ }
42
+
43
+ function ensureLocalDir(root) {
44
+ fs.mkdirSync(path.join(root, '.parche'), { recursive: true });
45
+ const exclude = path.join(root, '.git', 'info', 'exclude');
46
+ if (fs.existsSync(path.dirname(exclude))) {
47
+ const cur = fs.existsSync(exclude) ? fs.readFileSync(exclude, 'utf8') : '';
48
+ if (!cur.includes('.parche/')) fs.appendFileSync(exclude, '\n.parche/\n');
49
+ }
50
+ }
51
+
52
+ function printFindings(result) {
53
+ for (const pack of result.packs) {
54
+ const fs2 = result.findings.filter((f) => f.pack === pack.id);
55
+ if (!fs2.length) continue;
56
+ const d = pack.deadline ? ` · vence ${pack.deadline} (${daysUntil(pack.deadline)} días)` : '';
57
+ console.log(`\n■ ${pack.title}\n ${pack.provider} · severidad ${pack.severity}${d}`);
58
+ for (const f of fs2) console.log(` ${LABEL[f.status].padEnd(16)} ${f.file}${f.line ? ':' + f.line : ''} ${f.message}`);
59
+ }
60
+ }
61
+
62
+ async function main() {
63
+ const args = parseArgs(process.argv.slice(2));
64
+ const [cmd, dirArg] = args._;
65
+ if (!cmd || args.help) { console.log(HELP); return 0; }
66
+
67
+ if (cmd === 'packs') {
68
+ for (const p of loadPacks()) {
69
+ console.log(`${p.id.padEnd(36)} ${p.provider.padEnd(16)} ${p.kind.padEnd(10)} ${p.severity.padEnd(9)} ${p.deadline || '—'} ${p.codemod ? 'con arreglo' : 'solo detección'}`);
70
+ }
71
+ return 0;
72
+ }
73
+
74
+ const root = path.resolve(dirArg || '.');
75
+ if (!fs.existsSync(root)) throw new Error(`No existe ${root}`);
76
+ const packIds = args.pack ? [args.pack] : null;
77
+
78
+ if (cmd === 'scan') {
79
+ const result = run(root, { write: false, packIds });
80
+ ensureLocalDir(root);
81
+ fs.writeFileSync(path.join(root, '.parche', 'inventory.json'), JSON.stringify(result.inventory, null, 2));
82
+ fs.writeFileSync(path.join(root, '.parche', 'report.md'), scanReport({ ...result, changed: [] }));
83
+ record(root, { type: 'scan', findings: counts(result.findings) });
84
+ if (args.json) { console.log(JSON.stringify({ inventory: result.inventory, findings: result.findings }, null, 2)); return 0; }
85
+ console.log(`Parche · scan de ${path.basename(root)}\n`);
86
+ console.log('APIs encontradas:');
87
+ for (const [prov, p] of Object.entries(result.inventory.providers)) {
88
+ console.log(` ${prov.padEnd(12)} ${String(p.callSites).padStart(2)} usos en ${p.files.length} archivo(s) [${Object.keys(p.ops).join(', ')}]`);
89
+ }
90
+ printFindings(result);
91
+ console.log(`\n${summary(result.findings)}`);
92
+ console.log(`Reporte: ${path.join(root, '.parche', 'report.md')}`);
93
+ return 0;
94
+ }
95
+
96
+ if (cmd === 'fix') {
97
+ const result = run(root, { write: true, packIds });
98
+ ensureLocalDir(root);
99
+ fs.writeFileSync(path.join(root, '.parche', 'report.md'), scanReport(result));
100
+ record(root, { type: 'fix', packs: packIds, changed: result.changed.map((c) => c.file) });
101
+ console.log(result.changed.length ? 'Archivos modificados:' : 'Nada para modificar.');
102
+ for (const c of result.changed) console.log(` ${c.file} (${c.packs.join(', ')})`);
103
+ console.log(`\n${summary(result.findings)}`);
104
+ return 0;
105
+ }
106
+
107
+ if (cmd === 'pr') {
108
+ if (!git.isRepo(root)) throw new Error('`parche pr` necesita un repositorio git.');
109
+ if (!git.isClean(root)) throw new Error('Hay cambios sin commitear. Commiteá o guardá (stash) antes de correr `parche pr`.');
110
+ ensureLocalDir(root);
111
+ const base = git.currentBranch(root);
112
+ const packs = loadPacks().filter((p) => p.codemod && (!packIds || packIds.includes(p.id)));
113
+ for (const pack of packs) {
114
+ const preview = run(root, { write: false, packIds: [pack.id] });
115
+ if (!preview.findings.length) continue;
116
+ const branch = `parche/${pack.id}`;
117
+ git.checkoutNewBranch(root, branch, base);
118
+ const result = run(root, { write: true, packIds: [pack.id] });
119
+ const changedFiles = result.changed.map((c) => c.file);
120
+ const verification = args.verify || args['dry-run'] ? verify(root, changedFiles, { runTests: !!args.verify }) : null;
121
+ if (verification && !verification.ok) {
122
+ git.checkout(root, base);
123
+ throw new Error(`La verificación falló para ${pack.id}; no se abre el PR.\n${JSON.stringify(verification, null, 2)}`);
124
+ }
125
+ const body = prBody({ pack, findings: result.findings, changed: result.changed, verification });
126
+ const bodyPath = path.join(root, '.parche', `pr-${pack.id}.md`);
127
+ fs.writeFileSync(bodyPath, body);
128
+ const c = counts(result.findings);
129
+ const draft = !!(c['needs-review'] || c['needs-input'] || c.blocked);
130
+ const title = `Parche: ${pack.title}`;
131
+ console.log(`\n■ ${title}`);
132
+ if (!changedFiles.length) {
133
+ console.log(' Sin cambios de código: queda como issue/reporte (ver cuerpo del PR).');
134
+ git.checkout(root, base);
135
+ record(root, { type: 'report-only', pack: pack.id, findings: c });
136
+ continue;
137
+ }
138
+ const sha = git.commitAll(root, `${title}\n\n${pack.summary_es}\nPack ${pack.id} v${pack.version}.`);
139
+ console.log(` Rama ${branch} · commit ${sha} · ${changedFiles.length} archivo(s)${verification ? ` · verificación ${verification.ok ? 'OK' : 'FALLÓ'}` : ''}`);
140
+ console.log(` ${summary(result.findings)}`);
141
+ console.log(` Cuerpo del PR: ${bodyPath}`);
142
+ const remote = parseGithubRemote(git.originUrl(root));
143
+ let url = null;
144
+ if (!args['dry-run'] && process.env.GITHUB_TOKEN && remote) {
145
+ git.push(root, branch);
146
+ url = await openPullRequest({ token: process.env.GITHUB_TOKEN, ...remote, head: branch, base, title, body, draft });
147
+ console.log(` PR abierto${draft ? ' (borrador)' : ''}: ${url}`);
148
+ } else {
149
+ console.log(` Modo prueba: no se abrió el PR${draft ? ' (iría como borrador por tener ítems para revisar)' : ''}.`);
150
+ }
151
+ record(root, { type: 'pr', pack: pack.id, packVersion: pack.version, branch, sha, url, draft, files: changedFiles, findings: c });
152
+ git.checkout(root, base);
153
+ }
154
+ return 0;
155
+ }
156
+
157
+ if (cmd === 'feedback') {
158
+ if (!args.pack || !['merged', 'edited', 'rejected'].includes(args.outcome)) throw new Error('Uso: parche feedback <dir> --pack <id> --outcome merged|edited|rejected');
159
+ record(root, { type: 'feedback', pack: args.pack, outcome: args.outcome, note: args.note || null });
160
+ console.log(`Registrado: ${args.pack} → ${args.outcome}. Gracias, esto mejora el pack para todos.`);
161
+ return 0;
162
+ }
163
+
164
+ console.log(HELP);
165
+ return 1;
166
+ }
167
+
168
+ main().then((code) => process.exit(code)).catch((err) => {
169
+ console.error(`Error: ${err.message}`);
170
+ process.exit(1);
171
+ });
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "parche-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Dependabot para APIs: detecta cambios de APIs de terceros en tu código y abre el PR con el arreglo.",
5
+ "keywords": [
6
+ "api",
7
+ "migration",
8
+ "codemod",
9
+ "dependabot",
10
+ "arca",
11
+ "afip",
12
+ "wsfe",
13
+ "mercadopago",
14
+ "github-action"
15
+ ],
16
+ "homepage": "https://github.com/Pepo08/parche#readme",
17
+ "bugs": "https://github.com/Pepo08/parche/issues",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Pepo08/parche.git"
21
+ },
22
+ "author": "Joaquín González",
23
+ "license": "MIT",
24
+ "type": "module",
25
+ "bin": {
26
+ "parche": "bin/parche.js"
27
+ },
28
+ "files": [
29
+ "bin",
30
+ "src",
31
+ "packs",
32
+ "templates"
33
+ ],
34
+ "scripts": {
35
+ "test": "node --test test/*.test.js",
36
+ "demo": "bash demo.sh"
37
+ },
38
+ "engines": {
39
+ "node": ">=22"
40
+ },
41
+ "dependencies": {
42
+ "@babel/parser": "^7.28.0",
43
+ "jscodeshift": "^17.3.0"
44
+ }
45
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "id": "ai.model-retirements",
3
+ "version": "2026.10.15",
4
+ "provider": "Modelos de IA",
5
+ "title": "Modelos y endpoints de IA retirados o por retirarse",
6
+ "kind": "retirement",
7
+ "severity": "high",
8
+ "deadline": null,
9
+ "last_verified": "2026-10-15",
10
+ "summary_es": "Tu código usa modelos o APIs de IA que ya no responden o tienen fecha de retiro.",
11
+ "why_es": "Los proveedores de modelos retiran IDs de modelo y endpoints con algunas semanas o meses de aviso. Después de la fecha, las llamadas fallan. Parche no reemplaza el modelo automáticamente porque cambiar de modelo cambia el comportamiento: te avisa y te dice a qué migrar.",
12
+ "sources": [
13
+ { "title": "Anthropic: Model deprecations", "url": "https://platform.claude.com/docs/en/about-claude/model-deprecations" },
14
+ { "title": "OpenAI Assistants API: cierre el 26/08/2026", "url": "https://dev.to/mr_manushukla/openai-assistants-api-shuts-down-26-august-2026-a-3-week-migration-sprint-plan-596b" }
15
+ ],
16
+ "detect": {
17
+ "packages": ["@anthropic-ai/sdk", "openai"],
18
+ "retired_models": {
19
+ "claude-3-opus-20240229": "2026-01-05",
20
+ "claude-3-7-sonnet-20250219": "2026-02-19",
21
+ "claude-3-5-haiku-20241022": "2026-02-19",
22
+ "claude-3-haiku-20240307": "2026-04-20",
23
+ "claude-sonnet-4-20250514": "2026-06-15",
24
+ "claude-opus-4-20250514": "2026-06-15",
25
+ "claude-opus-4-1-20250805": "2026-08-05"
26
+ },
27
+ "retirement_floors": {
28
+ "claude-sonnet-4-5-20250929": "2026-09-29",
29
+ "claude-haiku-4-5-20251001": "2026-10-15",
30
+ "claude-opus-4-5-20251101": "2026-11-24"
31
+ },
32
+ "retired_endpoints": [
33
+ { "pattern": ".beta.assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" },
34
+ { "pattern": ".beta.threads", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" },
35
+ { "pattern": "/v1/assistants", "retired": "2026-08-26", "replacement_es": "Responses API + Conversations API" }
36
+ ]
37
+ },
38
+ "codemod": null,
39
+ "review_checklist_es": [
40
+ "Elegir el modelo de reemplazo según la página de deprecaciones del proveedor.",
41
+ "Correr tus evals o pruebas de regresión de prompts antes de cambiar de modelo.",
42
+ "Revisar parámetros que cambian entre generaciones de modelos."
43
+ ]
44
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "id": "arca.wsfe.condicion-iva-receptor",
3
+ "version": "2026.10.15",
4
+ "provider": "ARCA (ex AFIP)",
5
+ "title": "Condición frente al IVA del receptor obligatoria en WSFEv1",
6
+ "kind": "breaking",
7
+ "severity": "critical",
8
+ "deadline": "2026-12-01",
9
+ "last_verified": "2026-10-15",
10
+ "summary_es": "Desde el 1/12/2026 ARCA rechaza automáticamente las solicitudes de CAE que no informen CondicionIVAReceptorId.",
11
+ "why_es": "La RG 5616/2024 hizo obligatorio informar la condición frente al IVA del receptor en cada comprobante electrónico. El manual del desarrollador de WSFEv1 (v4.8) fija el 1 de diciembre de 2026 como fecha desde la cual el campo es obligatorio: si viaja vacío, nulo o no viaja, el pedido de CAE se rechaza y no se puede facturar.",
12
+ "sources": [
13
+ { "title": "VATupdate: Argentina makes recipient VAT status mandatory (WSFEv1 v4.8)", "url": "https://www.vatupdate.com/2026/09/09/argentina-makes-recipient-vat-status-mandatory-in-electronic-invoices/" },
14
+ { "title": "iProfesional: ARCA rechazará facturas sin la condición del cliente frente al IVA", "url": "https://www.iprofesional.com/impuestos/464104-nuevo-requisito-arca-puede-trabar-emision-facturas" },
15
+ { "title": "Tabla de códigos de FEParamGetCondicionIvaReceptor", "url": "https://app.spomsolutions.com/blog/condicion-iva-receptor-rg-5616" }
16
+ ],
17
+ "detect": {
18
+ "packages": ["@afipsdk/afip.js", "afip.js"],
19
+ "calls": ["ElectronicBilling.createVoucher", "ElectronicBilling.createNextVoucher"],
20
+ "raw_markers": ["FECAESolicitar", "FECAEDetRequest"],
21
+ "field": "CondicionIVAReceptorId"
22
+ },
23
+ "codes": {
24
+ "1": "IVA Responsable Inscripto",
25
+ "4": "IVA Sujeto Exento",
26
+ "5": "Consumidor Final",
27
+ "6": "Responsable Monotributo",
28
+ "7": "Sujeto No Categorizado",
29
+ "8": "Proveedor del Exterior",
30
+ "9": "Cliente del Exterior",
31
+ "10": "IVA Liberado – Ley N° 19.640",
32
+ "13": "Monotributista Social",
33
+ "15": "IVA No Alcanzado",
34
+ "16": "Monotributo Trabajador Independiente Promovido"
35
+ },
36
+ "codes_by_class": {
37
+ "A": [1, 6, 13, 16],
38
+ "B": [4, 5, 7, 8, 9, 10, 15],
39
+ "C": [1, 4, 5, 6, 7, 8, 9, 10, 13, 15, 16]
40
+ },
41
+ "codes_note_es": "Validá los códigos contra FEParamGetCondicionIvaReceptor en homologación antes de pasar a producción.",
42
+ "codemod": "arca-wsfe-condicion-iva",
43
+ "review_checklist_es": [
44
+ "Probar la emisión en homologación (wsfehomo) con cada tipo de comprobante que emitís.",
45
+ "Confirmar que cada cliente con CUIT tiene guardada su condición frente al IVA y que se envía en cada CAE.",
46
+ "Pedir la condición frente al IVA en el alta de clientes nuevos.",
47
+ "Validar los códigos con FEParamGetCondicionIvaReceptor (la tabla puede cambiar)."
48
+ ]
49
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "id": "mercadopago.payments-to-orders",
3
+ "version": "2026.10.15",
4
+ "provider": "Mercado Pago",
5
+ "title": "Migración de creación de pagos: Payments API → Orders API",
6
+ "kind": "migration",
7
+ "severity": "medium",
8
+ "deadline": null,
9
+ "last_verified": "2026-10-15",
10
+ "summary_es": "Mercado Pago dejó de sumar features a Payments API; Orders API es la integración nueva para Checkout API.",
11
+ "why_es": "Payments API sigue funcionando pero solo recibe correcciones de seguridad y estabilidad. Orders API unifica el procesamiento online, trae varias transacciones por orden, captura dedicada, 3DS 2.0 nativo y errores consolidados. Los pagos ya creados se siguen consultando, capturando, cancelando y reembolsando por la API con la que se crearon, así que Parche migra solo la creación y deja el resto marcado para revisión.",
12
+ "sources": [
13
+ { "title": "Mercado Pago: Cómo migrar de Payments API a Orders API", "url": "https://www.mercadopago.com.ar/developers/es/docs/checkout-api-orders/resources/migrate-payments-to-orders" },
14
+ { "title": "SDK Node.js de Mercado Pago (clase Order)", "url": "https://www.npmjs.com/package/mercadopago" }
15
+ ],
16
+ "detect": {
17
+ "packages": ["mercadopago"],
18
+ "calls": ["Payment.create", "Payment.get", "Payment.search", "Payment.cancel", "Payment.capture", "PaymentRefund"],
19
+ "raw_markers": ["api.mercadopago.com/v1/payments"],
20
+ "legacy_v1": ["mercadopago.configure(", "mercadopago.payment.create(", "mercadopago.payment.save("]
21
+ },
22
+ "status_map": {
23
+ "approved": "processed",
24
+ "rejected": "failed",
25
+ "cancelled": "canceled",
26
+ "pending": "action_required",
27
+ "in_process": "processing",
28
+ "authorized": "action_required",
29
+ "in_mediation": "charged_back",
30
+ "refunded": "refunded",
31
+ "charged_back": "charged_back"
32
+ },
33
+ "status_detail_map": {
34
+ "cc_rejected_bad_filled_card_number": "bad_filled_card_data",
35
+ "cc_rejected_bad_filled_date": "bad_filled_card_data",
36
+ "cc_rejected_bad_filled_security_code": "bad_filled_card_data",
37
+ "cc_rejected_call_for_authorize": "required_call_for_authorize",
38
+ "cc_rejected_card_disabled": "card_disabled",
39
+ "cc_rejected_duplicated_payment": "processing_error",
40
+ "cc_rejected_high_risk": "high_risk",
41
+ "cc_rejected_insufficient_amount": "insufficient_amount",
42
+ "cc_rejected_invalid_installments": "invalid_installments",
43
+ "cc_rejected_max_attempts": "max_attempts_exceeded",
44
+ "cc_rejected_other_reason": "rejected_by_issuer",
45
+ "pending_waiting_transfer": "waiting_transfer"
46
+ },
47
+ "codemod": "mercadopago-payments-to-orders",
48
+ "review_checklist_es": [
49
+ "Configurar el webhook con el tópico `orders` en Tus integraciones y mantener `payment` en paralelo durante la transición.",
50
+ "Enviar `X-Idempotency-Key` en todas las escrituras (crear, capturar, cancelar, reembolsar).",
51
+ "Manejar `status` y `status_detail` a nivel orden y a nivel transacción.",
52
+ "Persistir también `transactions.payments[].id` (los reembolsos parciales lo necesitan).",
53
+ "En sandbox, el email del pagador tiene que ser test@testuser.com.",
54
+ "Si usás captura manual: la ventana de captura es de 5 días y Orders solo soporta captura total.",
55
+ "Pagos creados con Payments API se siguen consultando y reembolsando por Payments API."
56
+ ]
57
+ }