Ring Platform Logo

    Loading Documentation Hub...

    Scanning documentation library

    Documentation

    Welcome — mission & audiences

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Library hub

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Getting Started

    Index
    Prerequisites
    Installation
    Database migrations
    First Success Validation
    Troubleshooting
    Next Steps

    Architecture

    Index
    Backend modes and databases
    Data Model
    Authentication Architecture
    Email AI-CRM Architecture
    PaymentConductor architecture
    Refcodes architecture
    News Kingdom architecture
    Proxy and internationalization
    Real Time
    Security

    Features

    Platform Features
    Authentication
    Email AI-CRM System
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Web3 Wallet
    Multi-Vendor Store
    Ring ERP
    Inventory & Stock
    Vendor Management

    API

    Index
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet
    Store API

    CLI

    Ring CLI (enterprise only)

    Customization

    Index
    Branding
    Features
    Localization
    Themes
    Components

    Deployment

    Index
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery

    Development

    Index
    Local Setup
    Code Structure
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise

    Roadmap

    Platform Roadmap (Technical)

    Examples

    Index
    Quick Start
    Authentication
    Email AI-CRM Tutorial
    Api Integration
    Web3 Integration
    White Label
    Real World

    Integrations

    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Library hub
    Welcome — mission & audiences
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP
    Deploy (Docker · k8s)
    Security & compliance reads
    Ring Platform Logo

    Loading Documentation Hub...

    Scanning documentation library

    Documentation

    Welcome — mission & audiences

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Library hub

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Getting Started

    Index
    Prerequisites
    Installation
    Database migrations
    First Success Validation
    Troubleshooting
    Next Steps

    Architecture

    Index
    Backend modes and databases
    Data Model
    Authentication Architecture
    Email AI-CRM Architecture
    PaymentConductor architecture
    Refcodes architecture
    News Kingdom architecture

    Features

    Platform Features
    Authentication
    Email AI-CRM System
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Web3 Wallet

    API

    Index
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet
    Store API

    CLI

    Ring CLI (enterprise only)

    Customization

    Index
    Branding
    Features
    Localization
    Themes
    Components

    Deployment

    Index
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery

    Development

    Index
    Local Setup
    Code Structure
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise

    Roadmap

    Platform Roadmap (Technical)

    Examples

    Index
    Quick Start
    Authentication
    Email AI-CRM Tutorial
    Api Integration
    Web3 Integration
    White Label
    Real World

    Integrations

    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Library hub
    Welcome — mission & audiences
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP
    Deploy (Docker · k8s)
    Security & compliance reads
    Ring Platform Logo

    Loading Documentation Hub...

    Scanning documentation library

    Documentation

    Welcome — mission & audiences

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Library hub

    Welcome to Ring Platform - Gateway Between Humanity and the Quantum World

    Getting Started

    Index
    Prerequisites
    Installation
    Database migrations
    First Success Validation
    Troubleshooting
    Next Steps

    Architecture

    Index
    Backend modes and databases
    Data Model
    Authentication Architecture
    Email AI-CRM Architecture
    PaymentConductor architecture
    Refcodes architecture
    News Kingdom architecture

    Features

    Platform Features
    Authentication
    Email AI-CRM System
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Web3 Wallet

    API

    Index
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet
    Store API

    CLI

    Ring CLI (enterprise only)

    Customization

    Index
    Branding
    Features
    Localization
    Themes
    Components

    Deployment

    Index
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery

    Development

    Index
    Local Setup
    Code Structure
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise

    Roadmap

    Platform Roadmap (Technical)

    Examples

    Index
    Quick Start
    Authentication
    Email AI-CRM Tutorial
    Api Integration
    Web3 Integration
    White Label
    Real World

    Integrations

    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Library hub
    Welcome — mission & audiences
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP
    Deploy (Docker · k8s)
    Security & compliance reads

    Referral Codes (Refcodes)

    The refcodes module turns Ring Platform into a referral growth engine: each connected wallet gets a random shareable code, first-time buyers can be attributed to a referrer, and referrers earn project tokens minted on-chain by a server-side operator wallet.

    Architecture: Refcodes architecture · Migrations: Getting started: migrations · Clone install: scripts/install-refcodes-module.sh

    Overview

    LayerResponsibility
    Attribution?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch)
    SignupNew users persist users.data.referredBy from ring_ref on first sign-in
    CheckoutOrder stores referralCode, referrerUserId, referrerWallet; review step shows badge
    LedgerPostgreSQL referral_rewards — off-chain state + mint tracking
    ERP railSame resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions
    On-chainUUPS ReferralRewards contract — idempotent payReferral per order

    Business rules

    • One code per wallet — 8-character random code (Base58-style alphabet, no ambiguous chars).
    • First purchase only — buyer with any prior paid order is not attributed.
    • No self-referral — referrer user ID or any linked buyer wallet blocks attribution.
    • Idempotent rewards — one reward row per orderReference; contract rejects duplicate orderRef hashes.
    • Fiat vs crypto rails — WayForPay (rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.

    User journey

    Routes and UI

    RouteAccessPurpose
    /refcodesAuthenticatedList codes per wallet, copy share URLs, reward history
    /admin/refcodesAdminApprove/reject pending fiat rewards, trigger mint
    GET /api/refcodesSessionJSON: codes + stats for current user
    POST /api/refcodes/mintAdminBatch-mint approved rewards
    POST /api/refcodes/trackPublicVisit beacon — increments visits on the refcode
    GET /api/cron/refcodes-mintCron (CRON_SECRET)Processes approved rewards queue (up to 20 per run)

    Share URL format: {APP_URL}?ref={CODE} (locale prefix optional; cookie is set on any landing page).

    ReferralAttributionEffect (public layout) fires the visit beacon when ring_ref_visible is present. Checkout Review step shows a referred badge when that cookie exists.

    Attribution flow

    1. proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.
    2. Signup — Auth.js signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.
    3. POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.
    4. On payment success:
      • WayForPay webhook (handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat').
      • Credit checkout (/api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).
    5. Membership upgrade — handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).

    Reward calculation

    Token rewards use the same resolveReferralCommissionPercent hierarchy as ERP settlement (features/store/lib/referral-commission.ts). For mixed carts, computeWeightedReferralPercentFromCart yields a subtotal-weighted effective percent stored as rewardPercent on each referral_rewards record.

    typescript
    
    // Simplified — see features/refcodes/services/referral-reward-service.ts
    const rewardPercent = computeWeightedReferralPercentFromCart(order.items, merchantConfigByEntityId)
    const usdValue = orderTotalInUsd * (rewardPercent / 100)
    const { token_amount } = await priceOracleService.convertUsdToRing(usdValue)
    Env varDefaultMeaning
    REFERRAL_REWARD_PERCENT5Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset
    REFERRAL_COMMISSION_MAX_PERCENT50Upper bound for all resolved referral rates
    REFERRAL_UAH_PER_USD40UAH → USD for WayForPay orders
    REFERRAL_CHAIN_ID137Polygon mainnet

    Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).

    Reward status machine

    StatusMeaning
    pending_approvalFiat order paid; awaiting admin
    approvedReady to mint (credit path skips pending)
    mintingTransaction submitted
    mintedOn-chain success; txHash stored
    failedMint reverted or RPC error; failureReason set
    rejectedAdmin rejected fiat reward

    Smart contract

    ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.

    Role / settingHolder
    DEFAULT_ADMIN_ROLEDeployer — upgrade, pause, token/mode config
    OPERATOR_ROLEServer minter wallet — calls payReferral
    rewardMode0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER
    solidity
    
    function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
        external onlyRole(OPERATOR_ROLE) whenNotPaused

    orderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.

    Deploy

    bash
    
    cd contracts
    rm -rf node_modules package-lock.json && npm install
    REFERRAL_REWARD_TOKEN_ADDRESS=0xYourToken \
    REFERRAL_MINTER_ADDRESS=0xOperator \
    REFERRAL_REWARD_MODE=0 \
    DEPLOYER_PRIVATE_KEY=0x... \
    npx hardhat run scripts/deploy-referral-rewards.js --network polygon

    Save the proxy address as REFERRAL_REWARDS_ADDRESS.

    Grant minter permission (MINT mode)

    The token must allow the ReferralRewards proxy to mint:

    Token typeAction
    Ownable mint (e.g. test MockMintableToken)token.transferOwnership(referralRewardsProxy)
    OpenZeppelin AccessControltoken.grantRole(MINTER_ROLE, referralRewardsProxy)
    Custom Ring tokenGrant your project's mint permission to the proxy

    The operator wallet (REFERRAL_MINTER_PRIVATE_KEY) only needs OPERATOR_ROLE on ReferralRewards — set in initialize(admin, operator, token, mode) at deploy.

    Database

    Migration 005_refcodes_schema.sql creates:

    TableCollection keyPurpose
    refcodesrefcodesCode document; id = code string
    referral_rewardsreferral_rewardsReward ledger

    Dev database name: ring_platform on ring-postgres-dev (see infrastructure/postgres/init/README.md). This is not ring_file_registry or clone DBs like ring_greenfood_live.

    bash
    
    # Dev
    export DATABASE_URL=postgresql://ring_user:ring_password_2024@localhost:5432/ring_platform
    ./scripts/apply-refcodes-migrations-dev.sh
    
    # Prod (k8s)
    K8S_NAMESPACE=ring-platform-org POSTGRES_DB=ring_platform POSTGRES_USER=ring_user \
      ./scripts/apply-refcodes-migrations-prod.sh

    Environment variables

    VariableRequiredDescription
    REFERRAL_MINTER_PRIVATE_KEYYes (mint)Server wallet with OPERATOR_ROLE — never expose to client
    REFERRAL_REWARDS_ADDRESSYesUUPS proxy address
    REFERRAL_REWARD_TOKEN_ADDRESSYesMintable ERC20 on target chain
    REFERRAL_REWARD_PERCENTNoDefault 5
    REFERRAL_CHAIN_IDNoDefault 137
    REFERRAL_UAH_PER_USDNoDefault 40
    REFERRAL_REWARD_MODENo0 MINT, 1 TRANSFER
    POLYGON_RPC_URLYes (mint)Server RPC for viem
    REFERRAL_MINTER_ADDRESSNoDeploy script operator fallback
    DEPLOYER_PRIVATE_KEYDeploy onlyHardhat deployer
    CRON_SECRETProd cronBearer token for GET /api/cron/refcodes-mint

    Copy env.local.template to .env.local and fill the REFERRAL CODES MODULE block per clone. Add secrets to .reggie-propagate-exclude.json when propagating to white-label clones.

    Module layout

    text
    
    features/refcodes/
      constants.ts          # Cookie name, collection names, env defaults
      types.ts
      abi/referral-rewards.json
      services/
        refcode-service.ts       # Generate/list/resolve codes
        attribution-service.ts   # Cookie → order attribution guards
        referral-reward-service.ts
        reward-minter.ts         # viem payReferral
    lib/web3/server-wallet.ts    # Operator wallet client
    app/(authenticated)/[locale]/refcodes/
    app/(admin)/[locale]/admin/refcodes/
    app/api/refcodes/

    Propagation to clones

    Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:

    bash
    
    ./scripts/install-refcodes-module.sh /path/to/clone

    Copies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).

    Operations

    • On-chain deploy and env checklist: REFERRAL-ONCHAIN-OPS.md (repo root).
    • Cron mint: Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.
    • After mint: referral_reward_minted in-app notification to the referrer.

    Related

    • Ring ERP — Commissions
    • Multi-Vendor Store
    • Payment Integration
    • PaymentConductor
    • Web3 Wallet

    Referral Codes (Refcodes)

    The refcodes module turns Ring Platform into a referral growth engine: each connected wallet gets a random shareable code, first-time buyers can be attributed to a referrer, and referrers earn project tokens minted on-chain by a server-side operator wallet.

    Architecture: Refcodes architecture · Migrations: Getting started: migrations · Clone install: scripts/install-refcodes-module.sh

    Overview

    LayerResponsibility
    Attribution?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch)
    SignupNew users persist users.data.referredBy from ring_ref on first sign-in
    CheckoutOrder stores referralCode, referrerUserId, referrerWallet; review step shows badge
    LedgerPostgreSQL referral_rewards — off-chain state + mint tracking
    ERP railSame resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions
    On-chainUUPS ReferralRewards contract — idempotent payReferral per order

    Business rules

    • One code per wallet — 8-character random code (Base58-style alphabet, no ambiguous chars).
    • First purchase only — buyer with any prior paid order is not attributed.
    • No self-referral — referrer user ID or any linked buyer wallet blocks attribution.
    • Idempotent rewards — one reward row per orderReference; contract rejects duplicate orderRef hashes.
    • Fiat vs crypto rails — WayForPay (rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.

    User journey

    Routes and UI

    RouteAccessPurpose
    /refcodesAuthenticatedList codes per wallet, copy share URLs, reward history
    /admin/refcodesAdminApprove/reject pending fiat rewards, trigger mint
    GET /api/refcodesSessionJSON: codes + stats for current user
    POST /api/refcodes/mintAdminBatch-mint approved rewards
    POST /api/refcodes/trackPublicVisit beacon — increments visits on the refcode
    GET /api/cron/refcodes-mintCron (CRON_SECRET)Processes approved rewards queue (up to 20 per run)

    Share URL format: {APP_URL}?ref={CODE} (locale prefix optional; cookie is set on any landing page).

    ReferralAttributionEffect (public layout) fires the visit beacon when ring_ref_visible is present. Checkout Review step shows a referred badge when that cookie exists.

    Attribution flow

    1. proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.
    2. Signup — Auth.js signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.
    3. POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.
    4. On payment success:
      • WayForPay webhook (handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat').
      • Credit checkout (/api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).
    5. Membership upgrade — handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).

    Reward calculation

    Token rewards use the same resolveReferralCommissionPercent hierarchy as ERP settlement (features/store/lib/referral-commission.ts). For mixed carts, computeWeightedReferralPercentFromCart yields a subtotal-weighted effective percent stored as rewardPercent on each referral_rewards record.

    typescript
    
    // Simplified — see features/refcodes/services/referral-reward-service.ts
    const rewardPercent = computeWeightedReferralPercentFromCart(order.items, merchantConfigByEntityId)
    const usdValue = orderTotalInUsd * (rewardPercent / 100)
    const { token_amount } = await priceOracleService.convertUsdToRing(usdValue)
    Env varDefaultMeaning
    REFERRAL_REWARD_PERCENT5Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset
    REFERRAL_COMMISSION_MAX_PERCENT50Upper bound for all resolved referral rates
    REFERRAL_UAH_PER_USD40UAH → USD for WayForPay orders
    REFERRAL_CHAIN_ID137Polygon mainnet

    Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).

    Reward status machine

    StatusMeaning
    pending_approvalFiat order paid; awaiting admin
    approvedReady to mint (credit path skips pending)
    mintingTransaction submitted
    mintedOn-chain success; txHash stored
    failedMint reverted or RPC error; failureReason set
    rejectedAdmin rejected fiat reward

    Smart contract

    ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.

    Role / settingHolder
    DEFAULT_ADMIN_ROLEDeployer — upgrade, pause, token/mode config
    OPERATOR_ROLEServer minter wallet — calls payReferral
    rewardMode0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER
    solidity
    
    function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
        external onlyRole(OPERATOR_ROLE) whenNotPaused

    orderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.

    Deploy

    bash
    
    cd contracts
    rm -rf node_modules package-lock.json && npm install
    REFERRAL_REWARD_TOKEN_ADDRESS=0xYourToken \
    REFERRAL_MINTER_ADDRESS=0xOperator \
    REFERRAL_REWARD_MODE=0 \
    DEPLOYER_PRIVATE_KEY=0x... \
    npx hardhat run scripts/deploy-referral-rewards.js --network polygon

    Save the proxy address as REFERRAL_REWARDS_ADDRESS.

    Grant minter permission (MINT mode)

    The token must allow the ReferralRewards proxy to mint:

    Token typeAction
    Ownable mint (e.g. test MockMintableToken)token.transferOwnership(referralRewardsProxy)
    OpenZeppelin AccessControltoken.grantRole(MINTER_ROLE, referralRewardsProxy)
    Custom Ring tokenGrant your project's mint permission to the proxy

    The operator wallet (REFERRAL_MINTER_PRIVATE_KEY) only needs OPERATOR_ROLE on ReferralRewards — set in initialize(admin, operator, token, mode) at deploy.

    Database

    Migration 005_refcodes_schema.sql creates:

    TableCollection keyPurpose
    refcodesrefcodesCode document; id = code string
    referral_rewardsreferral_rewardsReward ledger

    Dev database name: ring_platform on ring-postgres-dev (see infrastructure/postgres/init/README.md). This is not ring_file_registry or clone DBs like ring_greenfood_live.

    bash
    
    # Dev
    export DATABASE_URL=postgresql://ring_user:ring_password_2024@localhost:5432/ring_platform
    ./scripts/apply-refcodes-migrations-dev.sh
    
    # Prod (k8s)
    K8S_NAMESPACE=ring-platform-org POSTGRES_DB=ring_platform POSTGRES_USER=ring_user \
      ./scripts/apply-refcodes-migrations-prod.sh

    Environment variables

    VariableRequiredDescription
    REFERRAL_MINTER_PRIVATE_KEYYes (mint)Server wallet with OPERATOR_ROLE — never expose to client
    REFERRAL_REWARDS_ADDRESSYesUUPS proxy address
    REFERRAL_REWARD_TOKEN_ADDRESSYesMintable ERC20 on target chain
    REFERRAL_REWARD_PERCENTNoDefault 5
    REFERRAL_CHAIN_IDNoDefault 137
    REFERRAL_UAH_PER_USDNoDefault 40
    REFERRAL_REWARD_MODENo0 MINT, 1 TRANSFER
    POLYGON_RPC_URLYes (mint)Server RPC for viem
    REFERRAL_MINTER_ADDRESSNoDeploy script operator fallback
    DEPLOYER_PRIVATE_KEYDeploy onlyHardhat deployer
    CRON_SECRETProd cronBearer token for GET /api/cron/refcodes-mint

    Copy env.local.template to .env.local and fill the REFERRAL CODES MODULE block per clone. Add secrets to .reggie-propagate-exclude.json when propagating to white-label clones.

    Module layout

    text
    
    features/refcodes/
      constants.ts          # Cookie name, collection names, env defaults
      types.ts
      abi/referral-rewards.json
      services/
        refcode-service.ts       # Generate/list/resolve codes
        attribution-service.ts   # Cookie → order attribution guards
        referral-reward-service.ts
        reward-minter.ts         # viem payReferral
    lib/web3/server-wallet.ts    # Operator wallet client
    app/(authenticated)/[locale]/refcodes/
    app/(admin)/[locale]/admin/refcodes/
    app/api/refcodes/

    Propagation to clones

    Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:

    bash
    
    ./scripts/install-refcodes-module.sh /path/to/clone

    Copies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).

    Operations

    • On-chain deploy and env checklist: REFERRAL-ONCHAIN-OPS.md (repo root).
    • Cron mint: Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.
    • After mint: referral_reward_minted in-app notification to the referrer.

    Related

    • Ring ERP — Commissions
    • Multi-Vendor Store
    • Payment Integration
    • PaymentConductor
    • Web3 Wallet

    Referral Codes (Refcodes)

    The refcodes module turns Ring Platform into a referral growth engine: each connected wallet gets a random shareable code, first-time buyers can be attributed to a referrer, and referrers earn project tokens minted on-chain by a server-side operator wallet.

    Architecture: Refcodes architecture · Migrations: Getting started: migrations · Clone install: scripts/install-refcodes-module.sh

    Overview

    LayerResponsibility
    Attribution?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch)
    SignupNew users persist users.data.referredBy from ring_ref on first sign-in
    CheckoutOrder stores referralCode, referrerUserId, referrerWallet; review step shows badge
    LedgerPostgreSQL referral_rewards — off-chain state + mint tracking
    ERP railSame resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions
    On-chainUUPS ReferralRewards contract — idempotent payReferral per order

    Business rules

    • One code per wallet — 8-character random code (Base58-style alphabet, no ambiguous chars).
    • First purchase only — buyer with any prior paid order is not attributed.
    • No self-referral — referrer user ID or any linked buyer wallet blocks attribution.
    • Idempotent rewards — one reward row per orderReference; contract rejects duplicate orderRef hashes.
    • Fiat vs crypto rails — WayForPay (rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.

    User journey

    Routes and UI

    RouteAccessPurpose
    /refcodesAuthenticatedList codes per wallet, copy share URLs, reward history
    /admin/refcodesAdminApprove/reject pending fiat rewards, trigger mint
    GET /api/refcodesSessionJSON: codes + stats for current user
    POST /api/refcodes/mintAdminBatch-mint approved rewards
    POST /api/refcodes/trackPublicVisit beacon — increments visits on the refcode
    GET /api/cron/refcodes-mintCron (CRON_SECRET)Processes approved rewards queue (up to 20 per run)

    Share URL format: {APP_URL}?ref={CODE} (locale prefix optional; cookie is set on any landing page).

    ReferralAttributionEffect (public layout) fires the visit beacon when ring_ref_visible is present. Checkout Review step shows a referred badge when that cookie exists.

    Attribution flow

    1. proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.
    2. Signup — Auth.js signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.
    3. POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.
    4. On payment success:
      • WayForPay webhook (handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat').
      • Credit checkout (/api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).
    5. Membership upgrade — handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).

    Reward calculation

    Token rewards use the same resolveReferralCommissionPercent hierarchy as ERP settlement (features/store/lib/referral-commission.ts). For mixed carts, computeWeightedReferralPercentFromCart yields a subtotal-weighted effective percent stored as rewardPercent on each referral_rewards record.

    typescript
    
    // Simplified — see features/refcodes/services/referral-reward-service.ts
    const rewardPercent = computeWeightedReferralPercentFromCart(order.items, merchantConfigByEntityId)
    const usdValue = orderTotalInUsd * (rewardPercent / 100)
    const { token_amount } = await priceOracleService.convertUsdToRing(usdValue)
    Env varDefaultMeaning
    REFERRAL_REWARD_PERCENT5Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset
    REFERRAL_COMMISSION_MAX_PERCENT50Upper bound for all resolved referral rates
    REFERRAL_UAH_PER_USD40UAH → USD for WayForPay orders
    REFERRAL_CHAIN_ID137Polygon mainnet

    Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).

    Reward status machine

    StatusMeaning
    pending_approvalFiat order paid; awaiting admin
    approvedReady to mint (credit path skips pending)
    mintingTransaction submitted
    mintedOn-chain success; txHash stored
    failedMint reverted or RPC error; failureReason set
    rejectedAdmin rejected fiat reward

    Smart contract

    ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.

    Role / settingHolder
    DEFAULT_ADMIN_ROLEDeployer — upgrade, pause, token/mode config
    OPERATOR_ROLEServer minter wallet — calls payReferral
    rewardMode0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER
    solidity
    
    function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
        external onlyRole(OPERATOR_ROLE) whenNotPaused

    orderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.

    Deploy

    bash
    
    cd contracts
    rm -rf node_modules package-lock.json && npm install
    REFERRAL_REWARD_TOKEN_ADDRESS=0xYourToken \
    REFERRAL_MINTER_ADDRESS=0xOperator \
    REFERRAL_REWARD_MODE=0 \
    DEPLOYER_PRIVATE_KEY=0x... \
    npx hardhat run scripts/deploy-referral-rewards.js --network polygon

    Save the proxy address as REFERRAL_REWARDS_ADDRESS.

    Grant minter permission (MINT mode)

    The token must allow the ReferralRewards proxy to mint:

    Token typeAction
    Ownable mint (e.g. test MockMintableToken)token.transferOwnership(referralRewardsProxy)
    OpenZeppelin AccessControltoken.grantRole(MINTER_ROLE, referralRewardsProxy)
    Custom Ring tokenGrant your project's mint permission to the proxy

    The operator wallet (REFERRAL_MINTER_PRIVATE_KEY) only needs OPERATOR_ROLE on ReferralRewards — set in initialize(admin, operator, token, mode) at deploy.

    Database

    Migration 005_refcodes_schema.sql creates:

    TableCollection keyPurpose
    refcodesrefcodesCode document; id = code string
    referral_rewardsreferral_rewardsReward ledger

    Dev database name: ring_platform on ring-postgres-dev (see infrastructure/postgres/init/README.md). This is not ring_file_registry or clone DBs like ring_greenfood_live.

    bash
    
    # Dev
    export DATABASE_URL=postgresql://ring_user:ring_password_2024@localhost:5432/ring_platform
    ./scripts/apply-refcodes-migrations-dev.sh
    
    # Prod (k8s)
    K8S_NAMESPACE=ring-platform-org POSTGRES_DB=ring_platform POSTGRES_USER=ring_user \
      ./scripts/apply-refcodes-migrations-prod.sh

    Environment variables

    VariableRequiredDescription
    REFERRAL_MINTER_PRIVATE_KEYYes (mint)Server wallet with OPERATOR_ROLE — never expose to client
    REFERRAL_REWARDS_ADDRESSYesUUPS proxy address
    REFERRAL_REWARD_TOKEN_ADDRESSYesMintable ERC20 on target chain
    REFERRAL_REWARD_PERCENTNoDefault 5
    REFERRAL_CHAIN_IDNoDefault 137
    REFERRAL_UAH_PER_USDNoDefault 40
    REFERRAL_REWARD_MODENo0 MINT, 1 TRANSFER
    POLYGON_RPC_URLYes (mint)Server RPC for viem
    REFERRAL_MINTER_ADDRESSNoDeploy script operator fallback
    DEPLOYER_PRIVATE_KEYDeploy onlyHardhat deployer
    CRON_SECRETProd cronBearer token for GET /api/cron/refcodes-mint

    Copy env.local.template to .env.local and fill the REFERRAL CODES MODULE block per clone. Add secrets to .reggie-propagate-exclude.json when propagating to white-label clones.

    Module layout

    text
    
    features/refcodes/
      constants.ts          # Cookie name, collection names, env defaults
      types.ts
      abi/referral-rewards.json
      services/
        refcode-service.ts       # Generate/list/resolve codes
        attribution-service.ts   # Cookie → order attribution guards
        referral-reward-service.ts
        reward-minter.ts         # viem payReferral
    lib/web3/server-wallet.ts    # Operator wallet client
    app/(authenticated)/[locale]/refcodes/
    app/(admin)/[locale]/admin/refcodes/
    app/api/refcodes/

    Propagation to clones

    Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:

    bash
    
    ./scripts/install-refcodes-module.sh /path/to/clone

    Copies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).

    Operations

    • On-chain deploy and env checklist: REFERRAL-ONCHAIN-OPS.md (repo root).
    • Cron mint: Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.
    • After mint: referral_reward_minted in-app notification to the referrer.

    Related

    • Ring ERP — Commissions
    • Multi-Vendor Store
    • Payment Integration
    • PaymentConductor
    • Web3 Wallet
    Commissions & Settlements
    Referral Codes (Refcodes)
    Payment Integration
    PaymentConductor
    WayForPay Payment Integration
    News Module - Digital Newspaper Experience
    Member Blogs
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Admin API
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    Api Examples
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Proxy and internationalization
    Real Time
    Security
    Multi-Vendor Store
    Ring ERP
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    Referral Codes (Refcodes)
    Payment Integration
    PaymentConductor
    WayForPay Payment Integration
    News Module - Digital Newspaper Experience
    Member Blogs
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Admin API
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    Api Examples
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Proxy and internationalization
    Real Time
    Security
    Multi-Vendor Store
    Ring ERP
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    Referral Codes (Refcodes)
    Payment Integration
    PaymentConductor
    WayForPay Payment Integration
    News Module - Digital Newspaper Experience
    Member Blogs
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Admin API
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    Api Examples
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Back
    Back
    Back