Skip to content

Commit e6ec953

Browse files
authored
Merge pull request #1501 from priscaenoch/feature/1377-subscription-validate-create-plan
fix(subscription): validate create_plan amount and interval_days
2 parents b38c834 + f3c4981 commit e6ec953

3 files changed

Lines changed: 74 additions & 1 deletion

File tree

contract/contracts/subscription/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub struct Subscription {
4545
| 7 | `InvalidFeeBps` | Fee basis points exceed 10 000 (100%) |
4646
| 8 | `InvalidTokenAddress` | Token address is the Stellar null/burn address |
4747
| 9 | `InvalidPrice` | Subscription price must be strictly positive |
48+
| 11 | `InvalidPlanParams` | Plan `amount` must be strictly positive and `interval_days` non-zero |
4849

4950
---
5051

@@ -85,7 +86,8 @@ pub fn create_plan(
8586
```
8687

8788
Registers a new billing plan for `creator`. Returns the assigned `plan_id` (auto-incremented from 1).
88-
Requires `creator` authorization. Panics with `Paused` if the contract is paused.
89+
Requires `creator` authorization. Panics with `Paused` if the contract is paused, or
90+
`InvalidPlanParams` if `amount <= 0` or `interval_days == 0`.
8991

9092
**Event** `plan_created` — topics: `(name, creator)`, data: `plan_id`
9193

contract/contracts/subscription/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl DataKey {
6969
/// | 8 | `InvalidTokenAddress` |
7070
/// | 9 | `InvalidPrice` |
7171
/// | 10 | `PlanNotFound` |
72+
/// | 11 | `InvalidPlanParams` |
7273
#[contracterror]
7374
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7475
pub enum Error {
@@ -92,6 +93,8 @@ pub enum Error {
9293
InvalidPrice = 9,
9394
/// Code 10 – plan ID does not exist; never created or out of range.
9495
PlanNotFound = 10,
96+
/// Code 11 – plan `amount` must be strictly positive and `interval_days` non-zero.
97+
InvalidPlanParams = 11,
9598
}
9699

97100
/// Stellar "null" account (GAAA...WHF) — not a valid fee recipient.
@@ -184,6 +187,9 @@ impl MyfansContract {
184187
if paused {
185188
panic_with_error!(&env, Error::Paused);
186189
}
190+
if amount <= 0 || interval_days == 0 {
191+
panic_with_error!(&env, Error::InvalidPlanParams);
192+
}
187193

188194
let count: u32 = env
189195
.storage()

contract/contracts/subscription/src/test.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,3 +1818,68 @@ fn test_cancel_paused_returns_typed_error() {
18181818
Err(Ok(SorobanError::from_contract_error(Error::Paused as u32)))
18191819
);
18201820
}
1821+
1822+
// ── create_plan parameter validation ─────────────────────────────────────────
1823+
1824+
/// create_plan rejects a zero amount with a typed InvalidPlanParams error.
1825+
#[test]
1826+
fn test_create_plan_rejects_zero_amount() {
1827+
let (env, client, admin, token, _token_admin) = setup_test();
1828+
let fee_recipient = Address::generate(&env);
1829+
client.init(&admin, &500, &fee_recipient, &token.address, &1000);
1830+
let creator = Address::generate(&env);
1831+
1832+
let result = client.try_create_plan(&creator, &token.address, &0, &30);
1833+
assert_eq!(
1834+
result,
1835+
Err(Ok(SorobanError::from_contract_error(
1836+
Error::InvalidPlanParams as u32,
1837+
)))
1838+
);
1839+
}
1840+
1841+
/// create_plan rejects a negative amount with a typed InvalidPlanParams error.
1842+
#[test]
1843+
fn test_create_plan_rejects_negative_amount() {
1844+
let (env, client, admin, token, _token_admin) = setup_test();
1845+
let fee_recipient = Address::generate(&env);
1846+
client.init(&admin, &500, &fee_recipient, &token.address, &1000);
1847+
let creator = Address::generate(&env);
1848+
1849+
let result = client.try_create_plan(&creator, &token.address, &-1, &30);
1850+
assert_eq!(
1851+
result,
1852+
Err(Ok(SorobanError::from_contract_error(
1853+
Error::InvalidPlanParams as u32,
1854+
)))
1855+
);
1856+
}
1857+
1858+
/// create_plan rejects a zero interval_days with a typed InvalidPlanParams error.
1859+
#[test]
1860+
fn test_create_plan_rejects_zero_interval_days() {
1861+
let (env, client, admin, token, _token_admin) = setup_test();
1862+
let fee_recipient = Address::generate(&env);
1863+
client.init(&admin, &500, &fee_recipient, &token.address, &1000);
1864+
let creator = Address::generate(&env);
1865+
1866+
let result = client.try_create_plan(&creator, &token.address, &1000, &0);
1867+
assert_eq!(
1868+
result,
1869+
Err(Ok(SorobanError::from_contract_error(
1870+
Error::InvalidPlanParams as u32,
1871+
)))
1872+
);
1873+
}
1874+
1875+
/// A valid plan (positive amount, non-zero interval_days) still succeeds.
1876+
#[test]
1877+
fn test_create_plan_accepts_valid_params() {
1878+
let (env, client, admin, token, _token_admin) = setup_test();
1879+
let fee_recipient = Address::generate(&env);
1880+
client.init(&admin, &500, &fee_recipient, &token.address, &1000);
1881+
let creator = Address::generate(&env);
1882+
1883+
let plan_id = client.create_plan(&creator, &token.address, &1000, &30);
1884+
assert_eq!(plan_id, 1);
1885+
}

0 commit comments

Comments
 (0)