AdvancedSubscriptionSystem

Description:

Smart contract deployed on Ethereum.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Interface for interaction with ReferralSystem
interface IReferralSystem {
    function processReferralReward(address referrer, address user, uint256 planId, uint256 purchaseId, uint256 originalPrice) external;
    function getReferralDiscount(address) external view returns (uint8);
    function resolveReferralCode(string calldata referralCode) external view returns (address);
}

contract AdvancedSubscriptionSystem {
    address payable public owner;
    bool public paused;
    
    // Address of the ReferralSystem contract
    address public referralSystemAddress;
    
    enum CorePlanType { Daily, Monthly, Quarterly, Yearly }
    
    struct Plan {
        string name;
        uint256 price;
        uint256 duration;
        bool isActive;
        uint256 createdAt;
    }
    
    mapping(uint8 => Plan) public corePlans;
    
    struct UserSubscription {
        uint256 expiryTime;
        uint256[] purchaseIds;
    }
    
    struct Purchase {
        address user;
        uint256 planId;
        uint256 pricePaid;
        uint256 purchaseTime;
        address referrer;
        bytes32 couponCode;
        uint256 subscriptionEnd;
    }
    
    mapping(uint256 => Purchase) public purchases;
    uint256 private nextPurchaseId = 1;
    
    mapping(address => UserSubscription) public userSubscriptions;
    
    struct DiscountCoupon {
        string title;
        uint8 discountPercentage;
        uint16 usageLimit;
        uint16 usageCount;
        uint256 startTime;
        uint256 endTime;
        bool isPlanRestricted;
        bool isUserRestricted;
        bool isActive;
    }
    
    mapping(bytes32 => mapping(uint256 => bool)) public couponPlanRestrictions;
    
    mapping(bytes32 => mapping(address => bool)) public couponUserRestrictions;
    
    mapping(bytes32 => DiscountCoupon) public discountCoupons;
    
    // Arrays for enumeration (avoiding block scanning)
    bytes32[] private allCouponCodes;
    mapping(bytes32 => bool) private couponExists;
    
    address[] private allUsers;
    mapping(address => bool) private userExists;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Contract is paused");
        _;
    }
    
    modifier hasValidSubscription() {
        require(
            userSubscriptions[msg.sender].expiryTime > block.timestamp, 
            "No valid subscription"
        );
        _;
    }
    
    modifier onlyReferralSystem() {
        require(msg.sender == referralSystemAddress, "Only ReferralSystem can call");
        _;
    }
    
    constructor() {
        owner = payable(msg.sender);
        
        corePlans[uint8(CorePlanType.Daily)] = Plan({
            name: "Daily",
            price: 1000000000000000,
            duration: 1 days,
            isActive: true,
            createdAt: block.timestamp
        });
        
        corePlans[uint8(CorePlanType.Monthly)] = Plan({
            name: "Monthly",
            price: 7000000000000000,
            duration: 30 days,
            isActive: true,
            createdAt: block.timestamp
        });
        
        corePlans[uint8(CorePlanType.Quarterly)] = Plan({
            name: "Quarterly",
            price: 14000000000000000,
            duration: 90 days,
            isActive: true,
            createdAt: block.timestamp
        });
        
        corePlans[uint8(CorePlanType.Yearly)] = Plan({
            name: "Yearly",
            price: 100000000000000000,
            duration: 365 days,
            isActive: true,
            createdAt: block.timestamp
        });
    }
    
    function transferOwnership(address payable newOwner) external onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        owner = newOwner;
    }
    
    function setPaused(bool _paused) external onlyOwner {
        paused = _paused;
    }
    
    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No balance to withdraw");
        
        (bool success, ) = owner.call{value: balance}("");
        require(success, "Withdrawal failed");
    }
    
    function revokeAccess(address user) external onlyOwner {
        userSubscriptions[user].expiryTime = block.timestamp;
    }
    
    function setReferralSystemAddress(address _referralSystemAddress) external onlyOwner {
        require(_referralSystemAddress != address(0), "Invalid address");
        referralSystemAddress = _referralSystemAddress;
    }
    
    function updateCorePlan(CorePlanType planType, uint256 newPrice, uint256 newDuration) 
        external 
        onlyOwner 
    {
        require(newPrice > 0, "Price must be greater than 0");
        require(newDuration > 0, "Duration must be greater than 0");
        
        uint8 planTypeId = uint8(planType);
        Plan storage plan = corePlans[planTypeId];
        
        plan.price = newPrice;
        plan.duration = newDuration;
    }
    
    function setCorePlanStatus(CorePlanType planType, bool isActive) 
         
        external 
        onlyOwner 
    {
        uint8 planTypeId = uint8(planType);
        corePlans[planTypeId].isActive = isActive;
    }
    
    function createDiscountCoupon(
        bytes32 couponCode,
        string calldata title,
        uint8 discountPercentage,
        uint16 usageLimit,
        uint256 startTime,
        uint256 endTime
    ) 
        external 
        onlyOwner 
    {
        require(discountPercentage >= 10 && discountPercentage <= 100, "Discount must be between 10-100%");
        require(discountCoupons[couponCode].discountPercentage == 0, "Coupon code already exists");
        require(bytes(title).length > 0, "Title cannot be empty");
        
        if (endTime > 0) {
            require(endTime > startTime, "End time must be after start time");
        }
        
        if (startTime == 0) {
            startTime = block.timestamp;
        }
        
        discountCoupons[couponCode] = DiscountCoupon({
            title: title,
            discountPercentage: discountPercentage,
            usageLimit: usageLimit,
            usageCount: 0,
            startTime: startTime,
            endTime: endTime,
            isPlanRestricted: false,
            isUserRestricted: false,
            isActive: true
        });
        
        // Add to enumerable array if not exists
        if (!couponExists[couponCode]) {
            allCouponCodes.push(couponCode);
            couponExists[couponCode] = true;
        }
    }
    
    function updateDiscountCoupon(
        bytes32 couponCode,
        string calldata title,
        uint8 discountPercentage,
        uint16 usageLimit,
        bool isActive
    ) 
        external 
        onlyOwner 
    {
        require(discountCoupons[couponCode].discountPercentage > 0, "Coupon does not exist");
        require(discountPercentage >= 10 && discountPercentage <= 100, "Discount must be between 10-100%");
        require(bytes(title).length > 0, "Title cannot be empty");
        
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        
        coupon.title = title;
        coupon.discountPercentage = discountPercentage;
        coupon.usageLimit = usageLimit;
        coupon.isActive = isActive;
    }
    
    function setCouponPlanRestrictions(
        bytes32 couponCode,
        uint256[] calldata planIds
    ) 
        external 
        onlyOwner 
    {
        require(discountCoupons[couponCode].discountPercentage > 0, "Coupon does not exist");
        
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        coupon.isPlanRestricted = planIds.length > 0;
        
        for (uint256 i = 0; i < planIds.length; i++) {
            // Validate that the plans exist before adding restrictions
            require(planIds[i] < 4, "Invalid core plan ID");
            require(bytes(corePlans[uint8(planIds[i])].name).length > 0, "Core plan does not exist");
            
            // Set the restriction
            couponPlanRestrictions[couponCode][planIds[i]] = true;
        }
    }
    
    function setCouponUserRestrictions(
        bytes32 couponCode,
        address[] calldata users
    ) 
        external 
        onlyOwner 
    {
        require(discountCoupons[couponCode].discountPercentage > 0, "Coupon does not exist");
        
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        coupon.isUserRestricted = users.length > 0;
        
        for (uint256 i = 0; i < users.length; i++) {
            couponUserRestrictions[couponCode][users[i]] = true;
        }
    }
    
    function deleteDiscountCoupon(bytes32 couponCode) external onlyOwner {
        require(discountCoupons[couponCode].discountPercentage > 0, "Coupon does not exist");
        
        delete discountCoupons[couponCode];
    }
    
    function purchaseCorePlan(CorePlanType planType) 
        external 
        payable 
        whenNotPaused 
    {
        uint8 planTypeId = uint8(planType);
        Plan storage plan = corePlans[planTypeId];
        
        require(plan.isActive, "Plan is not active");
        require(msg.value >= plan.price, "Insufficient payment amount");
        
        _processSubscriptionPurchase(
            planTypeId,
            plan.price,
            plan.duration,
            address(0),
            bytes32(0)
        );
        
        if (msg.value > plan.price) {
            payable(msg.sender).transfer(msg.value - plan.price);
        }
    }
    
    
    function purchaseCorePlanWithReferralCode(CorePlanType planType, string calldata referralCode) 
        external 
        payable 
        whenNotPaused 
    {
        require(referralSystemAddress != address(0), "ReferralSystem not set");
        
        // Resolve referral code to address
        address referrer = IReferralSystem(referralSystemAddress).resolveReferralCode(referralCode);
        require(referrer != address(0), "Invalid referral code");
        require(referrer != msg.sender, "Cannot refer yourself");
        
        uint8 planTypeId = uint8(planType);
        Plan storage plan = corePlans[planTypeId];
        
        require(plan.isActive, "Plan is not active");
        
        // Get discount from ReferralSystem contract
        uint8 discountPercentage = IReferralSystem(referralSystemAddress).getReferralDiscount(referrer);
        uint256 discountedPrice = _calculateDiscountedPrice(plan.price, discountPercentage);
        
        require(msg.value >= discountedPrice, "Insufficient payment amount");
        
        uint256 purchaseId = nextPurchaseId;
        _processSubscriptionPurchase(
            planTypeId,
            discountedPrice,
            plan.duration,
            referrer,
            bytes32(0)
        );
        
        // Call process reward in ReferralSystem
        IReferralSystem(referralSystemAddress).processReferralReward(
            referrer,
            msg.sender, 
            planTypeId, 
            purchaseId,
            plan.price // Original price without discount
        );
        
        if (msg.value > discountedPrice) {
            payable(msg.sender).transfer(msg.value - discountedPrice);
        }
    }
    
    function purchaseCorePlanWithCoupon(CorePlanType planType, bytes32 couponCode) 
        external 
        payable 
        whenNotPaused 
    {
        uint8 planTypeId = uint8(planType);
        Plan storage plan = corePlans[planTypeId];
        
        require(plan.isActive, "Plan is not active");
        
        (bool isValid, uint8 discountPercentage) = _validateCoupon(couponCode, planTypeId);
        require(isValid, "Invalid or expired coupon");
        
        uint256 discountedPrice = _calculateDiscountedPrice(plan.price, discountPercentage);
        
        require(msg.value >= discountedPrice, "Insufficient payment amount");
        
        _processSubscriptionPurchase(
            planTypeId,
            discountedPrice,
            plan.duration,
            address(0),
            couponCode
        );
        
        _updateCouponUsage(couponCode);
        
        if (msg.value > discountedPrice) {
            payable(msg.sender).transfer(msg.value - discountedPrice);
        }
    }
    
    function _processSubscriptionPurchase(
        uint256 planId,
        uint256 amountPaid,
        uint256 duration,
        address referrer,
        bytes32 couponCode
    ) 
        internal 
    {
        uint256 purchaseId = nextPurchaseId++;
        
        uint256 expiryTime;
        if (userSubscriptions[msg.sender].expiryTime > block.timestamp) {
            expiryTime = userSubscriptions[msg.sender].expiryTime + duration;
        } else {
            expiryTime = block.timestamp + duration;
        }
        
        UserSubscription storage subscription = userSubscriptions[msg.sender];
        subscription.expiryTime = expiryTime;
        subscription.purchaseIds.push(purchaseId);
        
        // Add user to enumerable array if not exists
        if (!userExists[msg.sender]) {
            allUsers.push(msg.sender);
            userExists[msg.sender] = true;
        }
        
        purchases[purchaseId] = Purchase({
            user: msg.sender,
            planId: planId,
            pricePaid: amountPaid,
            purchaseTime: block.timestamp,
            referrer: referrer,
            couponCode: couponCode,
            subscriptionEnd: expiryTime
        });
    }
    
    function _validateCoupon(
        bytes32 couponCode,
        uint256 planId
    ) 
        internal 
        view 
        returns (bool isValid, uint8 discountPercentage) 
    {
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        
        if (coupon.discountPercentage == 0) {
            return (false, 0);
        }
        
        if (!coupon.isActive) {
            return (false, 0);
        }
        
        if (coupon.usageLimit > 0 && coupon.usageCount >= coupon.usageLimit) {
            return (false, 0);
        }
        
        if (block.timestamp < coupon.startTime) {
            return (false, 0);
        }
        
        if (coupon.endTime > 0 && block.timestamp > coupon.endTime) {
            return (false, 0);
        }
        
        if (coupon.isPlanRestricted) {
            // Check plan restrictions for core plans
            if (!couponPlanRestrictions[couponCode][planId]) {
            return (false, 0);
            }
            
            // Validate that plan exists
            if (planId >= 4 || bytes(corePlans[uint8(planId)].name).length == 0) {
                return (false, 0);
            }
        }
        
        if (coupon.isUserRestricted && !couponUserRestrictions[couponCode][msg.sender]) {
            return (false, 0);
        }
        
        return (true, coupon.discountPercentage);
    }
    
    function _updateCouponUsage(bytes32 couponCode) internal {
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        coupon.usageCount++;
    }
    
    function _calculateDiscountedPrice(
        uint256 originalPrice,
        uint8 discountPercentage
    ) 
        internal 
        pure 
        returns (uint256) 
    {
        return (originalPrice * (100 - discountPercentage)) / 100;
    }
    
    function getPlanDetails(uint256 planId)
        external
        view
        returns (
            string memory name,
            uint256 price,
            uint256 duration,
            bool isActive
        )
    {
        require(planId < 4, "Invalid plan ID");
            Plan storage plan = corePlans[uint8(planId)];
            return (plan.name, plan.price, plan.duration, plan.isActive);
    }
    
    function getPurchaseDetails(uint256 purchaseId)
        external
        view
        returns (
            address user,
            uint256 planId,
            uint256 pricePaid,
            uint256 purchaseTime,
            address referrer,
            bytes32 couponCode,
            uint256 subscriptionEnd
        )
    {
        require(purchaseId > 0 && purchaseId < nextPurchaseId, "Invalid purchase ID");
        
        Purchase storage purchase = purchases[purchaseId];
        return (
            purchase.user,
            purchase.planId,
            purchase.pricePaid,
            purchase.purchaseTime,
            purchase.referrer,
            purchase.couponCode,
            purchase.subscriptionEnd
        );
    }
    
    function getUserPurchases(address user)
        external
        view
        returns (uint256[] memory purchaseIds)
    {
        return userSubscriptions[user].purchaseIds;
    }
    
    function hasSubscription(address user) external view returns (bool) {
        return userSubscriptions[user].expiryTime > block.timestamp;
    }
    
    function getSubscriptionExpiry(address user) external view returns (uint256) {
        return userSubscriptions[user].expiryTime;
    }
    
    function getRemainingTime(address user) external view returns (uint256) {
        uint256 expiryTime = userSubscriptions[user].expiryTime;
        if (expiryTime <= block.timestamp) {
            return 0;
        }
        return expiryTime - block.timestamp;
    }
    
    function getAllCouponCodes() external view returns (bytes32[] memory) {
        return allCouponCodes;
    }
    
    function getCouponCount() external view returns (uint256) {
        return allCouponCodes.length;
    }
    
    function getAllUsers() external view returns (address[] memory) {
        return allUsers;
    }
    
    function getUserCount() external view returns (uint256) {
        return allUsers.length;
    }
    
    function getCouponDetails(bytes32 couponCode)
        external
        view
        returns (
            string memory title,
            uint8 discountPercentage,
            uint16 usageLimit,
            uint16 usageCount,
            uint256 startTime,
            uint256 endTime,
            bool isPlanRestricted,
            bool isUserRestricted,
            bool isActive
        )
    {
        DiscountCoupon storage coupon = discountCoupons[couponCode];
        return (
            coupon.title,
            coupon.discountPercentage,
            coupon.usageLimit,
            coupon.usageCount,
            coupon.startTime,
            coupon.endTime,
            coupon.isPlanRestricted,
            coupon.isUserRestricted,
            coupon.isActive
        );
    }
    
    function getUserSubscriptionDetails(address user)
        external
        view
        returns (
            bool isActive,
            uint256 expiryTime,
            uint256 remainingTime,
            uint256 purchaseCount
        )
    {
        expiryTime = userSubscriptions[user].expiryTime;
        isActive = expiryTime > block.timestamp;
        
        if (isActive) {
            remainingTime = expiryTime - block.timestamp;
        } else {
            remainingTime = 0;
        }
        
        purchaseCount = userSubscriptions[user].purchaseIds.length;
    }
    
    function getAllPlanDetails()
        external
        view
        returns (
            string[] memory names,
            uint256[] memory prices,
            uint256[] memory durations,
            bool[] memory activeStatuses
        )
    {
        names = new string[](4);
        prices = new uint256[](4);
        durations = new uint256[](4);
        activeStatuses = new bool[](4);
        
        for (uint8 i = 0; i < 4; i++) {
            Plan storage plan = corePlans[i];
            names[i] = plan.name;
            prices[i] = plan.price;
            durations[i] = plan.duration;
            activeStatuses[i] = plan.isActive;
        }
    }
    
    function payReferralReward(address recipient, uint256 amount) 
        external 
        onlyReferralSystem 
        whenNotPaused 
    {
        require(recipient != address(0), "Invalid recipient");
        require(amount > 0, "Amount must be greater than 0");
        require(address(this).balance >= amount, "Insufficient contract balance");
        
        (bool success, ) = payable(recipient).call{value: amount}("");
        require(success, "Payment failed");
    }
    
    receive() external payable {}
}

Tags:
addr:0xda285c9f186accea57aa1d30e9b09e7e1121abe8|verified:true|block:23674450|tx:0x8c0fc11ecf34a8b085d5212342e3a369fbef47e7ccd9ae2071389118032be1c5|first_check:1761650436

Submitted on: 2025-10-28 12:20:38

Comments

Log in to comment.

No comments yet.