@saulwade/swl-ses 1.0.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/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Manejo de Errores Stripe y Estrategias de Reintento
|
|
2
|
+
|
|
3
|
+
Stripe clasifica los errores en categorías con semántica distinta. Manejarlos
|
|
4
|
+
correctamente determina si el usuario ve un mensaje útil, si el sistema se
|
|
5
|
+
recupera solo, o si un pago legítimo se pierde por un error transitorio.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Tipos de errores Stripe
|
|
10
|
+
|
|
11
|
+
### Jerarquía de excepciones del SDK Python
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
stripe.error.StripeError # Base — todos los errores Stripe
|
|
15
|
+
├── stripe.error.CardError # Tarjeta declinada por el banco emisor
|
|
16
|
+
├── stripe.error.RateLimitError # Demasiadas peticiones (429)
|
|
17
|
+
├── stripe.error.InvalidRequestError # Parámetros inválidos (400)
|
|
18
|
+
├── stripe.error.AuthenticationError # API key inválida (401)
|
|
19
|
+
├── stripe.error.PermissionError # Sin permisos para la operación (403)
|
|
20
|
+
├── stripe.error.APIConnectionError # Error de red — Stripe no respondió
|
|
21
|
+
├── stripe.error.IdempotencyError # Idempotency key reutilizada incorrectamente
|
|
22
|
+
└── stripe.error.APIError # Error interno de Stripe (5xx)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Atributos clave en CardError
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
try:
|
|
29
|
+
stripe.PaymentIntent.create(...)
|
|
30
|
+
except stripe.error.CardError as e:
|
|
31
|
+
error = e.error
|
|
32
|
+
# error.code: 'card_declined', 'insufficient_funds', 'expired_card', etc.
|
|
33
|
+
# error.decline_code: sub-código del banco emisor
|
|
34
|
+
# error.message: mensaje técnico de Stripe (inglés)
|
|
35
|
+
# error.param: parámetro que causó el error (si aplica)
|
|
36
|
+
print(error.code, error.decline_code)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Clasificación por tipo y estrategia de manejo
|
|
42
|
+
|
|
43
|
+
| Error | Tipo | ¿Reintentar? | Acción recomendada |
|
|
44
|
+
|-------|------|-------------|-------------------|
|
|
45
|
+
| `card_declined` | CardError | No | Pedir nueva tarjeta al usuario |
|
|
46
|
+
| `insufficient_funds` | CardError | No | Informar fondos insuficientes |
|
|
47
|
+
| `expired_card` | CardError | No | Pedir nueva tarjeta |
|
|
48
|
+
| `incorrect_cvc` | CardError | No (max 3x) | Pedir CVV de nuevo |
|
|
49
|
+
| `authentication_required` | CardError | Sí (con 3DS) | Redirigir a 3D Secure |
|
|
50
|
+
| `rate_limit_error` | RateLimitError | Sí (backoff) | Retry con espera exponencial |
|
|
51
|
+
| `api_connection_error` | APIConnectionError | Sí (backoff) | Retry — error de red |
|
|
52
|
+
| `api_error` (5xx) | APIError | Sí (backoff) | Retry — error interno Stripe |
|
|
53
|
+
| `idempotency_error` | IdempotencyError | No | Revisar la key — bug en código |
|
|
54
|
+
| `invalid_request_error` | InvalidRequestError | No | Bug en la llamada — no reintentar |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Retry con backoff exponencial para errores transitorios
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import asyncio
|
|
62
|
+
import logging
|
|
63
|
+
import stripe
|
|
64
|
+
from typing import TypeVar, Callable, Awaitable
|
|
65
|
+
|
|
66
|
+
logger = logging.getLogger(__name__)
|
|
67
|
+
T = TypeVar("T")
|
|
68
|
+
|
|
69
|
+
# Errores que justifican reintento automático
|
|
70
|
+
ERRORES_REINTENTABLES = (
|
|
71
|
+
stripe.error.RateLimitError,
|
|
72
|
+
stripe.error.APIConnectionError,
|
|
73
|
+
stripe.error.APIError,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def stripe_con_retry(
|
|
78
|
+
operacion: Callable[[], T | Awaitable[T]],
|
|
79
|
+
max_reintentos: int = 3,
|
|
80
|
+
base_delay_segundos: float = 1.0,
|
|
81
|
+
operacion_nombre: str = "stripe_call",
|
|
82
|
+
) -> T:
|
|
83
|
+
"""
|
|
84
|
+
Ejecuta una operación de Stripe con retry exponencial.
|
|
85
|
+
Solo reintenta errores transitorios — nunca CardError ni errores de validación.
|
|
86
|
+
"""
|
|
87
|
+
ultimo_error: Exception | None = None
|
|
88
|
+
|
|
89
|
+
for intento in range(max_reintentos + 1):
|
|
90
|
+
try:
|
|
91
|
+
resultado = operacion()
|
|
92
|
+
if asyncio.iscoroutine(resultado):
|
|
93
|
+
return await resultado
|
|
94
|
+
return resultado
|
|
95
|
+
|
|
96
|
+
except ERRORES_REINTENTABLES as exc:
|
|
97
|
+
ultimo_error = exc
|
|
98
|
+
if intento == max_reintentos:
|
|
99
|
+
logger.error(
|
|
100
|
+
"Operación Stripe agotó reintentos",
|
|
101
|
+
operacion=operacion_nombre,
|
|
102
|
+
intentos=intento + 1,
|
|
103
|
+
error=str(exc),
|
|
104
|
+
)
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
# Backoff exponencial con jitter: 1s, 2s, 4s + ruido aleatorio
|
|
108
|
+
import random
|
|
109
|
+
delay = (base_delay_segundos * (2 ** intento)) + random.uniform(0, 0.5)
|
|
110
|
+
logger.warning(
|
|
111
|
+
"Error transitorio Stripe — reintentando",
|
|
112
|
+
operacion=operacion_nombre,
|
|
113
|
+
intento=intento + 1,
|
|
114
|
+
delay_segundos=round(delay, 2),
|
|
115
|
+
error_tipo=type(exc).__name__,
|
|
116
|
+
)
|
|
117
|
+
await asyncio.sleep(delay)
|
|
118
|
+
|
|
119
|
+
except (
|
|
120
|
+
stripe.error.CardError,
|
|
121
|
+
stripe.error.InvalidRequestError,
|
|
122
|
+
stripe.error.AuthenticationError,
|
|
123
|
+
stripe.error.IdempotencyError,
|
|
124
|
+
):
|
|
125
|
+
# Errores no reintentables — fallar inmediatamente
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
raise RuntimeError("No debería llegar aquí") from ultimo_error
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Uso
|
|
132
|
+
async def crear_intent_con_retry(orden: Orden) -> stripe.PaymentIntent:
|
|
133
|
+
return await stripe_con_retry(
|
|
134
|
+
lambda: stripe.PaymentIntent.create(
|
|
135
|
+
amount=int(orden.total * 100),
|
|
136
|
+
currency="mxn",
|
|
137
|
+
idempotency_key=f"pi_{orden.id}",
|
|
138
|
+
),
|
|
139
|
+
operacion_nombre=f"crear_payment_intent_{orden.id}",
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Manejo de declinación de tarjeta: mensajes user-friendly
|
|
146
|
+
|
|
147
|
+
Los mensajes técnicos de Stripe están en inglés y son inapropiados para mostrar
|
|
148
|
+
al usuario. Mapear a mensajes en español claros y útiles:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# services/mensajes_pago.py
|
|
152
|
+
|
|
153
|
+
MENSAJES_DECLINACION: dict[str | None, str] = {
|
|
154
|
+
# Códigos de CardError.code
|
|
155
|
+
"card_declined": "Tu tarjeta fue declinada. Verifica que los datos sean correctos o intenta con otra tarjeta.",
|
|
156
|
+
"insufficient_funds": "Tu tarjeta no tiene fondos suficientes para esta compra.",
|
|
157
|
+
"expired_card": "Tu tarjeta ha expirado. Por favor usa una tarjeta vigente.",
|
|
158
|
+
"incorrect_cvc": "El código de seguridad (CVV) es incorrecto. Revísalo e intenta de nuevo.",
|
|
159
|
+
"incorrect_number": "El número de tarjeta es incorrecto.",
|
|
160
|
+
"invalid_expiry_month": "El mes de vencimiento no es válido.",
|
|
161
|
+
"invalid_expiry_year": "El año de vencimiento no es válido.",
|
|
162
|
+
"card_velocity_exceeded": "Has excedido el límite de intentos. Intenta más tarde o usa otra tarjeta.",
|
|
163
|
+
"do_not_honor": "Tu banco no autorizó el cargo. Contacta a tu banco para más información.",
|
|
164
|
+
"fraudulent": "Esta transacción fue bloqueada por seguridad. Contacta a tu banco.",
|
|
165
|
+
"authentication_required": "Tu banco requiere autenticación adicional. Completa el proceso de verificación.",
|
|
166
|
+
# Fallback para códigos desconocidos
|
|
167
|
+
None: "No pudimos procesar tu pago. Verifica los datos de tu tarjeta o intenta con otra.",
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Códigos del banco emisor (decline_code) — más específicos
|
|
171
|
+
MENSAJES_DECLINE_CODE: dict[str, str] = {
|
|
172
|
+
"insufficient_funds": "Fondos insuficientes en tu tarjeta.",
|
|
173
|
+
"lost_card": "Esta tarjeta fue reportada como perdida. Contacta a tu banco.",
|
|
174
|
+
"stolen_card": "Esta tarjeta fue reportada como robada. Contacta a tu banco.",
|
|
175
|
+
"generic_decline": "Tu banco no autorizó el pago. Intenta con otra tarjeta o contacta a tu banco.",
|
|
176
|
+
"do_not_honor": "Tu banco no autorizó el cargo. Contacta a tu banco directamente.",
|
|
177
|
+
"card_not_supported": "Tu tarjeta no está habilitada para pagos en línea.",
|
|
178
|
+
"currency_not_supported": "Tu tarjeta no acepta pagos en esta moneda.",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def obtener_mensaje_declinacion(error: stripe.error.CardError) -> str:
|
|
183
|
+
"""Retorna un mensaje en español apropiado para mostrar al usuario."""
|
|
184
|
+
err = error.error
|
|
185
|
+
|
|
186
|
+
# Intentar con decline_code primero (más específico)
|
|
187
|
+
if err.decline_code and err.decline_code in MENSAJES_DECLINE_CODE:
|
|
188
|
+
return MENSAJES_DECLINE_CODE[err.decline_code]
|
|
189
|
+
|
|
190
|
+
# Luego con code
|
|
191
|
+
return MENSAJES_DECLINACION.get(
|
|
192
|
+
err.code,
|
|
193
|
+
MENSAJES_DECLINACION[None],
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Dunning management: reintentos automáticos en suscripciones
|
|
200
|
+
|
|
201
|
+
Cuando el cobro de una suscripción falla, Stripe implementa "dunning": reintentos
|
|
202
|
+
automáticos según el schedule configurado en el dashboard. El sistema maneja esto
|
|
203
|
+
de forma transparente, pero la app debe reaccionar a los eventos:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
# Configuración recomendada en el dashboard de Stripe (Smart Retries):
|
|
207
|
+
# - Reintento 1: 3 días después del fallo
|
|
208
|
+
# - Reintento 2: 5 días después del primer reintento
|
|
209
|
+
# - Reintento 3: 7 días después del segundo reintento
|
|
210
|
+
# - Después del último reintento: cancelar suscripción o marcar como unpaid
|
|
211
|
+
|
|
212
|
+
# La app debe manejar estos eventos para degradar o restaurar acceso:
|
|
213
|
+
|
|
214
|
+
async def manejar_invoice_payment_failed(invoice: dict) -> None:
|
|
215
|
+
"""
|
|
216
|
+
Se llama cuando Stripe no pudo cobrar una factura.
|
|
217
|
+
Estado de suscripción: active → past_due → unpaid → canceled
|
|
218
|
+
"""
|
|
219
|
+
from app.models.subscripciones import Subscripcion
|
|
220
|
+
from sqlalchemy import select, update
|
|
221
|
+
|
|
222
|
+
stripe_subscription_id = invoice.get("subscription")
|
|
223
|
+
if not stripe_subscription_id:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
# Obtener el número de intento para decidir la acción
|
|
227
|
+
attempt_count = invoice.get("attempt_count", 1)
|
|
228
|
+
|
|
229
|
+
async with AsyncSessionLocal() as db:
|
|
230
|
+
sub = await db.scalar(
|
|
231
|
+
select(Subscripcion).where(
|
|
232
|
+
Subscripcion.stripe_subscription_id == stripe_subscription_id
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
if sub is None:
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
if attempt_count == 1:
|
|
239
|
+
# Primer fallo — notificar al usuario, no restringir aún
|
|
240
|
+
await enviar_notificacion_pago_fallido(sub.usuario_id, intento=1)
|
|
241
|
+
elif attempt_count == 2:
|
|
242
|
+
# Segundo fallo — advertencia urgente
|
|
243
|
+
await enviar_notificacion_pago_fallido(sub.usuario_id, intento=2)
|
|
244
|
+
else:
|
|
245
|
+
# Tercer fallo o más — restringir acceso parcialmente
|
|
246
|
+
await db.execute(
|
|
247
|
+
update(Subscripcion)
|
|
248
|
+
.where(Subscripcion.id == sub.id)
|
|
249
|
+
.values(acceso_restringido=True)
|
|
250
|
+
)
|
|
251
|
+
await enviar_notificacion_suspension_inminente(sub.usuario_id)
|
|
252
|
+
await db.commit()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
async def manejar_subscripcion_cancelada_por_no_pago(sub: dict) -> None:
|
|
256
|
+
"""
|
|
257
|
+
Se llama cuando Stripe cancela la suscripción por reintentos agotados.
|
|
258
|
+
Revocar acceso completamente.
|
|
259
|
+
"""
|
|
260
|
+
async with AsyncSessionLocal() as db:
|
|
261
|
+
from sqlalchemy import update
|
|
262
|
+
from app.models.subscripciones import Subscripcion
|
|
263
|
+
|
|
264
|
+
await db.execute(
|
|
265
|
+
update(Subscripcion)
|
|
266
|
+
.where(Subscripcion.stripe_subscription_id == sub["id"])
|
|
267
|
+
.values(estado="cancelada", acceso_restringido=True)
|
|
268
|
+
)
|
|
269
|
+
await db.commit()
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Dispute handling: evidencia requerida por tipo de disputa
|
|
275
|
+
|
|
276
|
+
Una disputa (chargeback) ocurre cuando el tarjetahabiente impugna un cargo
|
|
277
|
+
ante su banco. Stripe genera el evento `charge.dispute.created`.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
async def manejar_disputa(disputa: stripe.Dispute) -> None:
|
|
281
|
+
"""
|
|
282
|
+
Responde automáticamente a disputas con evidencia disponible.
|
|
283
|
+
El plazo para responder es típicamente 7-21 días según el banco emisor.
|
|
284
|
+
"""
|
|
285
|
+
from app.models.ordenes import Orden
|
|
286
|
+
|
|
287
|
+
# Obtener la orden relacionada
|
|
288
|
+
payment_intent_id = disputa.payment_intent
|
|
289
|
+
orden = await Orden.objects.aget(
|
|
290
|
+
stripe_payment_intent_id=payment_intent_id
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Tipos de disputa y evidencia requerida
|
|
294
|
+
razon = disputa.reason
|
|
295
|
+
evidencia: dict = {}
|
|
296
|
+
|
|
297
|
+
if razon in ("fraudulent", "unrecognized"):
|
|
298
|
+
# El cliente dice no reconocer el cargo
|
|
299
|
+
evidencia = {
|
|
300
|
+
"billing_address": orden.direccion_facturacion,
|
|
301
|
+
"customer_email_address": orden.usuario.email,
|
|
302
|
+
"customer_ip_address": orden.ip_cliente,
|
|
303
|
+
"receipt": orden.url_recibo,
|
|
304
|
+
"shipping_documentation": orden.url_guia_envio,
|
|
305
|
+
"uncategorized_text": (
|
|
306
|
+
f"El cliente {orden.usuario.email} realizó la compra el "
|
|
307
|
+
f"{orden.creado_en.strftime('%d/%m/%Y')} desde la IP "
|
|
308
|
+
f"{orden.ip_cliente}. Se enviaron notificaciones de confirmación "
|
|
309
|
+
f"a su correo registrado."
|
|
310
|
+
),
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
elif razon == "product_not_received":
|
|
314
|
+
# El cliente dice no haber recibido el producto
|
|
315
|
+
evidencia = {
|
|
316
|
+
"shipping_documentation": orden.url_guia_envio,
|
|
317
|
+
"shipping_tracking_number": orden.numero_guia,
|
|
318
|
+
"shipping_carrier": orden.paqueteria,
|
|
319
|
+
"shipping_date": orden.fecha_envio.strftime("%Y-%m-%d"),
|
|
320
|
+
"uncategorized_text": (
|
|
321
|
+
f"El pedido fue enviado el {orden.fecha_envio.strftime('%d/%m/%Y')} "
|
|
322
|
+
f"por {orden.paqueteria} con guía {orden.numero_guia}. "
|
|
323
|
+
f"Estado de entrega: {orden.estado_entrega}."
|
|
324
|
+
),
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
elif razon == "duplicate":
|
|
328
|
+
# El cliente dice que el cargo está duplicado
|
|
329
|
+
evidencia = {
|
|
330
|
+
"duplicate_charge_id": disputa.charge,
|
|
331
|
+
"duplicate_charge_explanation": (
|
|
332
|
+
"Este cargo corresponde a una única transacción. "
|
|
333
|
+
f"El ID de orden es {orden.numero}."
|
|
334
|
+
),
|
|
335
|
+
"receipt": orden.url_recibo,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if evidencia:
|
|
339
|
+
stripe.Dispute.modify(
|
|
340
|
+
disputa.id,
|
|
341
|
+
evidence=evidencia,
|
|
342
|
+
submit=True, # Enviar inmediatamente a Stripe para revisión
|
|
343
|
+
)
|
|
344
|
+
logger.info(
|
|
345
|
+
"Evidencia de disputa enviada",
|
|
346
|
+
disputa_id=disputa.id,
|
|
347
|
+
razon=razon,
|
|
348
|
+
orden_id=str(orden.id),
|
|
349
|
+
)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Tipos de disputa y evidencia óptima
|
|
353
|
+
|
|
354
|
+
| Razón de disputa | Evidencia más efectiva |
|
|
355
|
+
|-----------------|----------------------|
|
|
356
|
+
| `fraudulent` | IP del cliente, email de confirmación, historial de compras |
|
|
357
|
+
| `unrecognized` | Email de confirmación, descripción del producto, IP |
|
|
358
|
+
| `product_not_received` | Guía de envío, número de rastreo, fecha de entrega |
|
|
359
|
+
| `product_unacceptable` | Política de devoluciones, comunicación con el cliente |
|
|
360
|
+
| `duplicate` | Explicación del cargo, recibo original |
|
|
361
|
+
| `subscription_canceled` | Fecha de cancelación, términos de servicio |
|
|
362
|
+
| `credit_not_processed` | Documentación del reembolso ya procesado |
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Alertas y monitoreo de salud de pagos
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
# Métricas clave a monitorear (enviar a tu sistema de alertas)
|
|
370
|
+
|
|
371
|
+
ALERTAS_PAGOS = {
|
|
372
|
+
# Tasa de declinación > 10% en 1 hora → posible fraude o problema técnico
|
|
373
|
+
"tasa_declinacion_alta": {
|
|
374
|
+
"umbral": 0.10,
|
|
375
|
+
"ventana_minutos": 60,
|
|
376
|
+
"severidad": "alta",
|
|
377
|
+
},
|
|
378
|
+
# Más de 3 disputas en 24 horas → revisar calidad del producto/servicio
|
|
379
|
+
"disputas_frecuentes": {
|
|
380
|
+
"umbral": 3,
|
|
381
|
+
"ventana_horas": 24,
|
|
382
|
+
"severidad": "media",
|
|
383
|
+
},
|
|
384
|
+
# Webhook sin procesar por más de 5 minutos → posible problema en el endpoint
|
|
385
|
+
"webhook_delayed": {
|
|
386
|
+
"umbral_minutos": 5,
|
|
387
|
+
"severidad": "alta",
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
```
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Stripe Connect — Marketplaces y Plataformas
|
|
2
|
+
|
|
3
|
+
Stripe Connect permite que una plataforma facilite pagos entre compradores
|
|
4
|
+
y vendedores (o prestadores de servicio), reteniendo una comisión y pagando
|
|
5
|
+
a los vendedores automáticamente. Es el sistema detrás de marketplaces como
|
|
6
|
+
Airbnb, Etsy o plataformas de freelancers.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Modelo de negocio: plataforma → vendedor → cliente
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Cliente
|
|
14
|
+
|
|
|
15
|
+
| paga $1,000 MXN
|
|
16
|
+
v
|
|
17
|
+
Plataforma (cuenta Stripe principal)
|
|
18
|
+
|
|
|
19
|
+
| transfiere $900 MXN al vendedor
|
|
20
|
+
| retiene $100 MXN como comisión
|
|
21
|
+
v
|
|
22
|
+
Vendedor (cuenta Stripe Connect — "connected account")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
La plataforma es responsable ante Stripe de los pagos. Los vendedores reciben
|
|
26
|
+
payouts directamente en su cuenta bancaria según el schedule configurado.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Tipos de cargos en Connect
|
|
31
|
+
|
|
32
|
+
### 1. Destination Charges (recomendado para la mayoría de casos)
|
|
33
|
+
|
|
34
|
+
El cargo se crea en la cuenta de la **plataforma** y se transfiere al vendedor.
|
|
35
|
+
La plataforma es responsable de disputas y reembolsos.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import stripe
|
|
39
|
+
from app.core.config import settings
|
|
40
|
+
|
|
41
|
+
stripe.api_key = settings.STRIPE_SECRET_KEY
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def crear_cobro_marketplace(
|
|
45
|
+
monto_total_centavos: int,
|
|
46
|
+
comision_plataforma_centavos: int,
|
|
47
|
+
stripe_account_id_vendedor: str,
|
|
48
|
+
orden_id: str,
|
|
49
|
+
) -> stripe.PaymentIntent:
|
|
50
|
+
"""
|
|
51
|
+
Crea un Destination Charge.
|
|
52
|
+
El cliente paga monto_total; el vendedor recibe (monto_total - comision).
|
|
53
|
+
"""
|
|
54
|
+
return stripe.PaymentIntent.create(
|
|
55
|
+
amount=monto_total_centavos,
|
|
56
|
+
currency="mxn",
|
|
57
|
+
# La comisión que retiene la plataforma
|
|
58
|
+
application_fee_amount=comision_plataforma_centavos,
|
|
59
|
+
# La cuenta Connect del vendedor recibe el resto
|
|
60
|
+
transfer_data={
|
|
61
|
+
"destination": stripe_account_id_vendedor,
|
|
62
|
+
},
|
|
63
|
+
metadata={"orden_id": orden_id},
|
|
64
|
+
idempotency_key=f"pi_marketplace_{orden_id}",
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Direct Charges (para mayor autonomía del vendedor)
|
|
69
|
+
|
|
70
|
+
El cargo se crea directamente en la cuenta del **vendedor**. La plataforma
|
|
71
|
+
toma su comisión vía `application_fee_amount`. El vendedor maneja disputas.
|
|
72
|
+
Requiere que el cliente conozca la marca del vendedor.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
async def crear_cobro_directo(
|
|
76
|
+
monto_centavos: int,
|
|
77
|
+
comision_centavos: int,
|
|
78
|
+
stripe_account_id_vendedor: str,
|
|
79
|
+
orden_id: str,
|
|
80
|
+
) -> stripe.PaymentIntent:
|
|
81
|
+
"""Cargo directo en la cuenta del vendedor."""
|
|
82
|
+
return stripe.PaymentIntent.create(
|
|
83
|
+
amount=monto_centavos,
|
|
84
|
+
currency="mxn",
|
|
85
|
+
application_fee_amount=comision_centavos,
|
|
86
|
+
metadata={"orden_id": orden_id},
|
|
87
|
+
idempotency_key=f"pi_direct_{orden_id}",
|
|
88
|
+
stripe_account=stripe_account_id_vendedor, # Clave: ejecutar en cuenta del vendedor
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Separate Charges and Transfers (control máximo)
|
|
93
|
+
|
|
94
|
+
Cobra al cliente en la cuenta de la plataforma y crea transferencias
|
|
95
|
+
independientes a uno o varios vendedores. Útil cuando un pedido tiene
|
|
96
|
+
productos de múltiples vendedores.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
async def distribuir_pago_multi_vendedor(
|
|
100
|
+
payment_intent_id: str,
|
|
101
|
+
distribuciones: list[dict], # [{"account": "acct_xxx", "amount": 500}]
|
|
102
|
+
) -> list[stripe.Transfer]:
|
|
103
|
+
"""
|
|
104
|
+
Transfiere partes del pago a múltiples vendedores.
|
|
105
|
+
Llamar DESPUÉS de que el PaymentIntent sea exitoso.
|
|
106
|
+
"""
|
|
107
|
+
transferencias = []
|
|
108
|
+
for dist in distribuciones:
|
|
109
|
+
transferencia = stripe.Transfer.create(
|
|
110
|
+
amount=dist["amount"],
|
|
111
|
+
currency="mxn",
|
|
112
|
+
destination=dist["account"],
|
|
113
|
+
source_transaction=payment_intent_id,
|
|
114
|
+
idempotency_key=f"transfer_{payment_intent_id}_{dist['account']}",
|
|
115
|
+
)
|
|
116
|
+
transferencias.append(transferencia)
|
|
117
|
+
return transferencias
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Onboarding de vendedores: AccountLink para KYC
|
|
123
|
+
|
|
124
|
+
Stripe requiere verificar la identidad de los vendedores (KYC — Know Your Customer)
|
|
125
|
+
antes de permitirles recibir pagos. Este proceso se realiza vía AccountLink.
|
|
126
|
+
|
|
127
|
+
### Paso 1: Crear la cuenta Connect del vendedor
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
async def crear_cuenta_vendedor(
|
|
131
|
+
email: str,
|
|
132
|
+
usuario_id: str,
|
|
133
|
+
) -> stripe.Account:
|
|
134
|
+
"""
|
|
135
|
+
Crea una cuenta Connect de tipo 'express' (recomendado).
|
|
136
|
+
- express: Stripe maneja el onboarding UI (dashboard simplificado para el vendedor)
|
|
137
|
+
- standard: el vendedor tiene acceso completo al dashboard de Stripe
|
|
138
|
+
- custom: la plataforma controla toda la UI (requiere más trabajo y responsabilidad)
|
|
139
|
+
"""
|
|
140
|
+
cuenta = stripe.Account.create(
|
|
141
|
+
type="express",
|
|
142
|
+
email=email,
|
|
143
|
+
country="MX",
|
|
144
|
+
capabilities={
|
|
145
|
+
"card_payments": {"requested": True},
|
|
146
|
+
"transfers": {"requested": True},
|
|
147
|
+
},
|
|
148
|
+
business_type="individual",
|
|
149
|
+
metadata={"usuario_id": usuario_id},
|
|
150
|
+
)
|
|
151
|
+
# Guardar cuenta.id como stripe_connect_account_id en la BD del vendedor
|
|
152
|
+
return cuenta
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def generar_link_onboarding(
|
|
156
|
+
stripe_account_id: str,
|
|
157
|
+
usuario_id: str,
|
|
158
|
+
) -> str:
|
|
159
|
+
"""
|
|
160
|
+
Genera un AccountLink para que el vendedor complete el KYC.
|
|
161
|
+
El link expira en ~10 minutos — generar justo antes de redirigir.
|
|
162
|
+
"""
|
|
163
|
+
link = stripe.AccountLink.create(
|
|
164
|
+
account=stripe_account_id,
|
|
165
|
+
refresh_url=f"{settings.BASE_URL}/vendedores/onboarding/reiniciar",
|
|
166
|
+
return_url=f"{settings.BASE_URL}/vendedores/onboarding/completado",
|
|
167
|
+
type="account_onboarding",
|
|
168
|
+
)
|
|
169
|
+
return link.url
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def verificar_onboarding_completo(stripe_account_id: str) -> bool:
|
|
173
|
+
"""Verifica si el vendedor completó el KYC y puede recibir pagos."""
|
|
174
|
+
cuenta = stripe.Account.retrieve(stripe_account_id)
|
|
175
|
+
return (
|
|
176
|
+
cuenta.details_submitted
|
|
177
|
+
and cuenta.charges_enabled
|
|
178
|
+
and cuenta.payouts_enabled
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Flujo de onboarding completo
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
1. Vendedor hace clic en "Conectar cuenta de pagos"
|
|
186
|
+
2. Backend llama crear_cuenta_vendedor() → guarda stripe_connect_account_id
|
|
187
|
+
3. Backend llama generar_link_onboarding() → redirige al vendedor a Stripe
|
|
188
|
+
4. Vendedor completa KYC en Stripe (CURP, datos bancarios, etc.)
|
|
189
|
+
5. Stripe redirige a return_url
|
|
190
|
+
6. Backend llama verificar_onboarding_completo() para confirmar
|
|
191
|
+
7. Stripe envía webhook account.updated cuando cambia el estado
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Splits de pago: application_fee_amount
|
|
197
|
+
|
|
198
|
+
La comisión de la plataforma se especifica en `application_fee_amount` (centavos).
|
|
199
|
+
Esta comisión se retiene automáticamente antes de transferir al vendedor.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# Ejemplo: orden de $1,000 MXN con 10% de comisión de plataforma
|
|
203
|
+
monto_total = 100_000 # $1,000.00 MXN en centavos
|
|
204
|
+
comision = 10_000 # $100.00 MXN (10%)
|
|
205
|
+
# El vendedor recibe: $900.00 MXN
|
|
206
|
+
|
|
207
|
+
# Cálculo dinámico de comisión
|
|
208
|
+
def calcular_comision(monto_centavos: int, porcentaje: float) -> int:
|
|
209
|
+
"""Retorna la comisión en centavos, redondeando hacia arriba."""
|
|
210
|
+
import math
|
|
211
|
+
return math.ceil(monto_centavos * porcentaje / 100)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Payout schedule para vendedores
|
|
217
|
+
|
|
218
|
+
Por defecto, Stripe realiza payouts automáticos a las cuentas bancarias de
|
|
219
|
+
los vendedores. Se puede configurar el schedule por cuenta:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
async def configurar_payout_schedule(
|
|
223
|
+
stripe_account_id: str,
|
|
224
|
+
intervalo: str = "weekly", # "daily" | "weekly" | "monthly"
|
|
225
|
+
dia_semana: str = "friday", # Para weekly
|
|
226
|
+
) -> stripe.Account:
|
|
227
|
+
"""
|
|
228
|
+
Configura cuándo recibe payouts el vendedor.
|
|
229
|
+
- daily: cada día hábil (requiere cuenta activa por ≥ 7 días)
|
|
230
|
+
- weekly: el día especificado de la semana
|
|
231
|
+
- monthly: el día especificado del mes (1-31)
|
|
232
|
+
"""
|
|
233
|
+
return stripe.Account.modify(
|
|
234
|
+
stripe_account_id,
|
|
235
|
+
settings={
|
|
236
|
+
"payouts": {
|
|
237
|
+
"schedule": {
|
|
238
|
+
"interval": intervalo,
|
|
239
|
+
"weekly_anchor": dia_semana,
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
stripe_account=stripe_account_id,
|
|
244
|
+
)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Webhooks críticos para Connect
|
|
250
|
+
|
|
251
|
+
Además de los webhooks estándar, Connect genera eventos para cuentas conectadas:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# El header stripe-signature sigue siendo obligatorio verificar
|
|
255
|
+
# Los eventos de cuentas conectadas incluyen account.updated
|
|
256
|
+
|
|
257
|
+
match evento.type:
|
|
258
|
+
case "account.updated":
|
|
259
|
+
# Verificar si el vendedor completó onboarding
|
|
260
|
+
cuenta = evento.data.object
|
|
261
|
+
await actualizar_estado_vendedor(cuenta.id, cuenta.charges_enabled)
|
|
262
|
+
case "account.application.deauthorized":
|
|
263
|
+
# El vendedor desconectó la plataforma — revocar acceso
|
|
264
|
+
await revocar_vendedor(evento.data.object.id)
|
|
265
|
+
case "transfer.created":
|
|
266
|
+
# Transferencia enviada al vendedor
|
|
267
|
+
await registrar_transferencia(evento.data.object)
|
|
268
|
+
case "payout.paid":
|
|
269
|
+
# El vendedor recibió el dinero en su banco
|
|
270
|
+
await marcar_payout_pagado(evento.data.object)
|
|
271
|
+
case "payout.failed":
|
|
272
|
+
# El payout falló (cuenta bancaria inválida, etc.)
|
|
273
|
+
await notificar_payout_fallido(evento.data.object)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## MUST DO / MUST NOT DO en Connect
|
|
279
|
+
|
|
280
|
+
**MUST DO:**
|
|
281
|
+
- Guardar `stripe_connect_account_id` en la BD del vendedor desde el momento de creación.
|
|
282
|
+
- Verificar `charges_enabled` Y `payouts_enabled` antes de permitir transacciones.
|
|
283
|
+
- Manejar `account.updated` para actualizar el estado de onboarding en tiempo real.
|
|
284
|
+
- Escalar `application_fee_amount` según la lógica de negocio de cada transacción.
|
|
285
|
+
|
|
286
|
+
**MUST NOT DO:**
|
|
287
|
+
- NUNCA crear un transfer sin `source_transaction` — vincula la transferencia al cobro.
|
|
288
|
+
- NUNCA hacer payouts manuales cuando el schedule automático es suficiente.
|
|
289
|
+
- NUNCA asumir que el return_url significa que el onboarding está completo — verificar con la API.
|
|
290
|
+
- NUNCA compartir el `stripe_account_id` de un vendedor con otros vendedores.
|