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 {}
}
Submitted on: 2025-10-28 12:20:38
Comments
Log in to comment.
No comments yet.