Creando un Sistema de Suscripciones Web3: Automatizando Pagos Recurrentes en Ethereal
Este tutorial práctico te guiará en la creación de un contrato inteligente de suscripciones recurrentes en Solidity. Descubrirás cómo manejar pagos automatizados, control de acceso basado en tiempo y la integración con dApps en Ethereum.
Introducción al Ecosistema de Suscripciones en Web3 💳
El modelo de negocio basado en suscripciones (SaaS, streaming, membresías) es uno de los pilares de la economía digital actual. Sin embargo, en el mundo tradicional, este modelo depende de intermediarios centralizados como pasarelas de pago y procesadores de tarjetas de crédito que cobran altas comisiones y pueden bloquear cuentas arbitrariamente.
En el ecosistema Web3, los contratos inteligentes nos permiten descentralizar este proceso. A diferencia de las transferencias tradicionales donde el usuario debe aprobar manualmente cada transacción, los contratos inteligentes pueden diseñarse para permitir cobros recurrentes autorizados bajo condiciones específicas y seguras.
¿Cómo Funcionan las Suscripciones en Blockchain? ⚙️
A nivel técnico, implementar suscripciones recurrentes en blockchains públicas como Ethereum presenta un desafío fundamental: la blockchain es un sistema pasivo. Un contrato inteligente no puede ejecutarse a sí mismo de manera autónoma; necesita que una transacción externa (una llamada) active sus funciones.
Para solucionar esto, utilizamos un enfoque híbrido:
- El Contrato Inteligente: Almacena la lógica de negocio, las tarifas, los períodos de gracia y los saldos de los suscriptores.
- Oráculos o Servicios de Automatización: Utilizaremos herramientas como Chainlink Keepers o scripts personalizados para activar el cobro periódico de manera automatizada.
Requisitos Previos y Configuración del Entorno 🛠️
Antes de escribir código, asegurémonos de tener nuestro entorno de desarrollo configurado correctamente. Necesitarás:
- Node.js (versión 18 o superior)
- Un gestor de paquetes como
npmoyarn - Un entorno de desarrollo como Hardhat
Vamos a inicializar nuestro proyecto de Hardhat ejecutando los siguientes comandos en tu terminal:
mkdir web3-subscriptions
cd web3-subscriptions
npm init -y
npm install --save-dev hardhat
npx hardhat
Selecciona la opción para crear un proyecto básico en JavaScript o TypeScript y asegúrate de instalar las dependencias de OpenZeppelin, ya que utilizaremos contratos estándar probados en seguridad:
npm install @openzeppelin/contracts
Diseñando el Contrato Inteligente de Suscripción 📝
Crearemos un contrato llamado SubscriptionManager.sol dentro de la carpeta contracts/. Este contrato permitirá a los usuarios suscribirse pagando una tarifa mensual en un token ERC-20 (como USDC) o en Ether nativo. Para simplificar este tutorial, utilizaremos Ether nativo.
Crea el archivo contracts/SubscriptionManager.sol y añade el siguiente código:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
contract SubscriptionManager is Ownable {
struct Subscription {
uint256 expiresAt;
uint256 monthlyFee;
bool isActive;
}
mapping(address => Subscription) public subscriptions;
uint256 public constant SUBSCRIPTION_PERIOD = 30 days;
uint256 public standardFee;
event Subscribed(address indexed user, uint256 expiresAt);
event Renewed(address indexed user, uint256 expiresAt);
event Cancelled(address indexed user);
constructor(uint256 _standardFee) Ownable(msg.sender) {
standardFee = _standardFee;
}
function subscribe() external payable {
require(msg.value >= standardFee, "Insufficient funds for subscription");
Subscription storage sub = subscriptions[msg.sender];
uint256 startTime = block.timestamp;
if (sub.isActive && sub.expiresAt > block.timestamp) {
startTime = sub.expiresAt;
}
sub.expiresAt = startTime + SUBSCRIPTION_PERIOD;
sub.monthlyFee = standardFee;
sub.isActive = true;
emit Subscribed(msg.sender, sub.expiresAt);
}
function cancelSubscription() external {
require(subscriptions[msg.sender].isActive, "No active subscription");
subscriptions[msg.sender].isActive = false;
emit Cancelled(msg.sender);
}
function checkSubscription(address user) external view returns (bool) {
return subscriptions[user].isActive && subscriptions[user].expiresAt > block.timestamp;
}
function withdrawFunds() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
}
Pruebas Unitarias con Hardhat 🧪
Es fundamental probar nuestro contrato antes de desplegarlo en una red de prueba. Crearemos un archivo de pruebas en test/SubscriptionManager.test.js para verificar el comportamiento de las suscripciones.
Crea el archivo de pruebas con el siguiente contenido:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SubscriptionManager", function () {
let subscriptionManager;
let owner;
let user1;
let fee;
beforeEach(async function () {
[owner, user1] = await ethers.getSigners();
fee = ethers.parseEther("0.1");
const SubscriptionManager = await ethers.getContractFactory("SubscriptionManager");
subscriptionManager = await SubscriptionManager.deploy(fee);
await subscriptionManager.waitForDeployment();
});
it("Should allow a user to subscribe", async function () {
await subscriptionManager.connect(user1).subscribe({ value: fee });
const isActive = await subscriptionManager.checkSubscription(user1.address);
expect(isActive).to.be.true;
});
it(
"Should allow a user to cancel subscription", async function () {
await subscriptionManager.connect(user1).subscribe({ value: fee });
await subscriptionManager.connect(user1).cancelSubscription();
const isActive = await subscriptionManager.checkSubscription(user1.address);
expect(isActive).to.be.false;
}
);
});
Ejecuta las pruebas en tu terminal con el siguiente comando:
npx hardhat test
Despliegue en Redes de Prueba 🚀
Una vez que las pruebas pasen exitosamente, podemos desplegar nuestro contrato en una red de pruebas como Sepolia. Crea un script de despliegue en scripts/deploy.js:
const { ethers } = require("hardhat");
async function main() {
const fee = ethers.parseEther("0.05"); // Tarifa de suscripción de ejemplo
const SubscriptionManager = await ethers.getContractFactory("SubscriptionManager");
console.log("Desplegando SubscriptionManager...");
const subscriptionManager = await SubscriptionManager.deploy(fee);
await subscriptionManager.waitForDeployment();
console.log(`SubscriptionManager desplegado en: ${await subscriptionManager.getAddress()}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Despliega el contrato ejecutando:
npx hardhat run scripts/deploy.js --network sepolia
Preguntas Frecuentes (FAQ) 📖
¿Cómo se manejan los pagos automáticos si el usuario no tiene fondos?
Si el usuario no cuenta con los fondos necesarios al momento del cobro automático, el contrato simplemente no renovará la suscripción y el estado `isActive` o la fecha de expiración reflejarán la caducidad del servicio, bloqueando el acceso automáticamente.¿Puedo reembolsar una suscripción si el usuario cancela anticipadamente?
Depende de la lógica de negocio que implementes. En nuestro contrato básico actual no se contemplan reembolsos automáticos, pero podrías agregar una función proporcional basada en el tiempo restante.Conclusión y Próximos Pasos ✨
¡Felicidades! Has diseñado, probado y preparado para despliegue un sistema completo de suscripciones basado en contratos inteligentes. Este modelo abre un sinfín de posibilidades para monetizar aplicaciones descentralizadas, servicios de contenido exclusivo y plataformas SaaS en Web3.
Como próximos pasos, te sugerimos:
- Integrar pagos con tokens estables (USDC / USDT).
- Conectar Chainlink Keepers para automatizar los cobros recurrentes sin intervención manual del usuario.
- Desarrollar una interfaz frontend utilizando React y Ethers.js / Viem para interactuar con tu dApp.
Tutoriales relacionados
- Creando y Utilizando un Mercado NFT Descentralizado con IPFS y Smart Contracts 🌐intermediate20 min
- Desarrollando DAOs Interoperables con Mensajería Cross-Chain: Un Tutorial con LayerZero 🌐intermediate20 min
- Explorando y Utilizando Identidades Auto-Soberanas (SSI) en Web3: Privacidad y Control Personal 🛡️intermediate18 min
- Explorando y Aplicando los Estándares de Metadatos Descentralizados para NFTs (ERC-721 y ERC-1155) en Web3 🎨intermediate18 min
- Tokenización de Activos Fungibles en Web3: Creando un Token ERC-20 con OpenZeppelin y Hardhat 💰intermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!