Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
5.0 kB
3
Indexable
Never
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SubscriptionService is Ownable {
    struct Plan {
        uint256 id;
        string name;
        uint256 price;
        string description;
        uint256 period;
    }

    struct Subscription {
        address customerAddress; // Added customerAddress field
        uint256 createdAt;
        uint256 updatedAt;
    }
    mapping(address => Plan[]) public plans;
    mapping(address => uint256) public merchantBalances;
    mapping(address => uint256) public activationFees;
    mapping(address => mapping(uint256 => bool)) public inactivePlans;
    mapping(uint256 => Subscription[]) public subscriptions;
    mapping(address => mapping(uint256 => bool)) public customerPlans;

    uint256 public totalServiceFees; // Total service fees collected

    event SubscriptionFailed(address indexed customer, uint256 indexed planId);
    event PlanAdded(uint256 indexed planId, address indexed merchant);
    event PlanRemoved(uint256 indexed planId, address indexed merchant);
    event PaymentTransferred(
        uint256 indexed planId,
        address indexed customerAddress,
        address indexed merchantAddress,
        uint256 amount
    );
    event TransferFailed(
        uint256 indexed planId,
        address indexed customerAddress
    );

    function addPlan(
        string memory name,
        uint256 price,
        string memory description,
        uint256 period
    ) public {
        uint256 planId = uint256(
            keccak256(abi.encodePacked(name, msg.sender, block.timestamp))
        );
        plans[msg.sender].push(Plan(planId, name, price, description, period));

        emit PlanAdded(planId, msg.sender);
    }

    function deactivatePlan(uint256 planId) public {
        inactivePlans[msg.sender][planId] = true;
        emit PlanRemoved(planId, msg.sender);
    }

    function subscribe(uint256 planId) public {
        Subscription memory newSubscription = Subscription(
            msg.sender,
            block.timestamp,
            0
        );
        subscriptions[planId].push(newSubscription);
    }

    function runner(address merchantAddress, IERC20 token) external {
        uint256 gasStart = gasleft();
        Plan[] storage merchantPlans = plans[merchantAddress];

        for (uint256 i = 0; i < merchantPlans.length; i++) {
            Plan storage plan = merchantPlans[i];
            if (inactivePlans[merchantAddress][plan.id]) continue; // Skip if inactive
            Subscription[] storage subs = subscriptions[plan.id];

            for (uint256 j = 0; j < subs.length; j++) {
                Subscription storage sub = subs[j];
                uint64 diff = uint64(block.timestamp) -
                    uint64(sub.updatedAt > 0 ? sub.updatedAt : sub.createdAt);

                if (diff >= plan.period) {
                    try
                        token.transferFrom(
                            sub.customerAddress,
                            address(this),
                            plan.price
                        )
                    {
                        merchantBalances[merchantAddress] += plan.price;
                        sub.updatedAt = uint64(block.timestamp);
                        emit PaymentTransferred(
                            plan.id,
                            sub.customerAddress,
                            merchantAddress,
                            plan.price
                        );
                    } catch {
                        emit TransferFailed(plan.id, sub.customerAddress);
                    }
                }
            }
        }

        uint256 gasEnd = gasleft();
        uint256 gasUsed = gasStart - gasEnd;
        uint256 activationFee = gasUsed * tx.gasprice;
        activationFees[merchantAddress] += activationFee;
    }

    function claimFunds(IERC20 token) external {
        uint256 merchantBalance = merchantBalances[msg.sender];
        uint256 activationFee = activationFees[msg.sender];
        uint256 serviceFee = (activationFee * 5) / 1000; // 0.5% of activation fee

        uint256 totalAmount = merchantBalance + activationFee - serviceFee;
        payable(msg.sender).transfer(activationFee - serviceFee); // Transfer activationFee in Ether

        totalServiceFees += serviceFee;

        require(token.transfer(msg.sender, totalAmount), "Transfer failed");

        merchantBalances[msg.sender] = 0;
        activationFees[msg.sender] = 0;
    }

    function withdrawServiceFees() external onlyOwner {
        uint256 amount = totalServiceFees;

        totalServiceFees = 0;

        payable(owner()).transfer(amount);
    }

    receive() external payable {}
}