From 4e5c7d707721d718dbcb6a1227661a3a2c824a3a Mon Sep 17 00:00:00 2001 From: Joshua Date: Tue, 17 Feb 2026 11:44:03 -0800 Subject: [PATCH] Add frozen membership-platform interfaces and API surface doc --- README.md | 2 + .../membership-platform-interfaces.md | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 docs/contracts/membership-platform-interfaces.md diff --git a/README.md b/README.md index 327e825..28b117f 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ docs/ roadmap-membership-platform.md review-notes.md platform-spec-alignment-review.md + contracts/ + membership-platform-interfaces.md schemas/ offer.v1.schema.json entitlement.v1.schema.json diff --git a/docs/contracts/membership-platform-interfaces.md b/docs/contracts/membership-platform-interfaces.md new file mode 100644 index 0000000..fb55409 --- /dev/null +++ b/docs/contracts/membership-platform-interfaces.md @@ -0,0 +1,114 @@ +# EDUT Membership Platform Interfaces (v1) + +This document freezes interface targets for membership-gated commerce. + +## Solidity Interfaces + +```solidity +interface IEdutMembership { + event MembershipMinted(address indexed wallet, uint256 indexed tokenId, uint256 amountPaid, address currency); + event MintPriceUpdated(address indexed currency, uint256 amountAtomic); + event MembershipStatusUpdated(address indexed wallet, uint8 status); + + function mintMembership(address recipient) external payable returns (uint256 tokenId); + function hasMembership(address wallet) external view returns (bool); + function membershipStatus(address wallet) external view returns (uint8 status); + function currentMintPrice() external view returns (address currency, uint256 amountAtomic); +} + +interface IEdutOfferRegistry { + event OfferUpserted(bytes32 indexed offerKey, bytes32 policyHash, address indexed issuer); + event OfferStatusChanged(bytes32 indexed offerKey, uint8 status); + + function upsertOffer(bytes32 offerKey, bytes32 policyHash, bytes calldata encodedOffer) external; + function setOfferStatus(bytes32 offerKey, uint8 status) external; + function getOffer(bytes32 offerKey) external view returns (bytes memory encodedOffer); +} + +interface IEdutEntitlement { + event EntitlementMinted(bytes32 indexed entitlementId, bytes32 indexed offerKey, address indexed wallet); + event EntitlementStateChanged(bytes32 indexed entitlementId, uint8 state); + + function mintEntitlement(bytes32 offerKey, address wallet, bytes32 policyHash) external returns (bytes32 entitlementId); + function entitlementState(bytes32 entitlementId) external view returns (uint8 state); +} +``` + +## Status Enums + +```text +MembershipStatus: +0 = NONE +1 = ACTIVE +2 = SUSPENDED +3 = REVOKED + +OfferStatus: +0 = DRAFT +1 = ACTIVE +2 = PAUSED +3 = RETIRED + +EntitlementState: +0 = ACTIVE +1 = SUSPENDED +2 = REVOKED +3 = EXPIRED +``` + +## API Surface (Backend) + +## Wallet + Membership + +1. `POST /secret/wallet/intent` +2. `POST /secret/wallet/verify` +3. `POST /secret/membership/quote` +4. `POST /secret/membership/confirm` +5. `GET /secret/membership/status?designation_code=...` + +## Marketplace + +1. `GET /marketplace/offers` +2. `GET /marketplace/offers/{offer_id}` +3. `POST /marketplace/checkout/quote` +4. `POST /marketplace/checkout/confirm` +5. `GET /marketplace/entitlements` + +## Issuer APIs + +1. `POST /issuer/offers/upsert` +2. `POST /issuer/offers/{offer_id}/status` +3. `POST /issuer/manifests/upsert` + +## Deterministic Gate Requirements + +1. Every checkout quote must include: + - `quote_id` + - `wallet` + - `offer_id` + - `currency` + - `amount_atomic` + - `policy_hash` + - `expires_at` +2. Checkout confirm must fail closed if: + - membership is not active, + - quote expired, + - tx amount/currency mismatch, + - policy hash mismatch. +3. Entitlement activation must fail closed when state is not `ACTIVE`. + +## Evidence Requirements + +Each successful purchase must emit/record: + +1. wallet +2. membership status snapshot +3. offer_id and policy_hash +4. quote_id +5. tx_hash and chain_id +6. entitlement_id +7. receipt timestamp + +## Compatibility Rule + +Interfaces can only evolve through additive changes in v1. Breaking changes require v2 namespace and migration plan.