Scanning documentation library
Scanning documentation library
Scanning documentation library
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
| Layer | Responsibility |
|---|---|
| Attribution | ?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch) |
| Signup | New users persist users.data.referredBy from ring_ref on first sign-in |
| Checkout | Order stores referralCode, referrerUserId, referrerWallet; review step shows badge |
| Ledger | PostgreSQL referral_rewards — off-chain state + mint tracking |
| ERP rail | Same resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions |
| On-chain | UUPS ReferralRewards contract — idempotent payReferral per order |
orderReference; contract rejects duplicate orderRef hashes.rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.| Route | Access | Purpose |
|---|---|---|
/refcodes | Authenticated | List codes per wallet, copy share URLs, reward history |
/admin/refcodes | Admin | Approve/reject pending fiat rewards, trigger mint |
GET /api/refcodes | Session | JSON: codes + stats for current user |
POST /api/refcodes/mint | Admin | Batch-mint approved rewards |
POST /api/refcodes/track | Public | Visit beacon — increments visits on the refcode |
GET /api/cron/refcodes-mint | Cron (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.
proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat')./api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).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.
// 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 var | Default | Meaning |
|---|---|---|
REFERRAL_REWARD_PERCENT | 5 | Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset |
REFERRAL_COMMISSION_MAX_PERCENT | 50 | Upper bound for all resolved referral rates |
REFERRAL_UAH_PER_USD | 40 | UAH → USD for WayForPay orders |
REFERRAL_CHAIN_ID | 137 | Polygon mainnet |
Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).
| Status | Meaning |
|---|---|
pending_approval | Fiat order paid; awaiting admin |
approved | Ready to mint (credit path skips pending) |
minting | Transaction submitted |
minted | On-chain success; txHash stored |
failed | Mint reverted or RPC error; failureReason set |
rejected | Admin rejected fiat reward |
ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.
| Role / setting | Holder |
|---|---|
DEFAULT_ADMIN_ROLE | Deployer — upgrade, pause, token/mode config |
OPERATOR_ROLE | Server minter wallet — calls payReferral |
rewardMode | 0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER |
function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
external onlyRole(OPERATOR_ROLE) whenNotPausedorderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.
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 polygonSave the proxy address as REFERRAL_REWARDS_ADDRESS.
The token must allow the ReferralRewards proxy to mint:
| Token type | Action |
|---|---|
Ownable mint (e.g. test MockMintableToken) | token.transferOwnership(referralRewardsProxy) |
OpenZeppelin AccessControl | token.grantRole(MINTER_ROLE, referralRewardsProxy) |
| Custom Ring token | Grant 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.
Migration 005_refcodes_schema.sql creates:
| Table | Collection key | Purpose |
|---|---|---|
refcodes | refcodes | Code document; id = code string |
referral_rewards | referral_rewards | Reward 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.
# 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| Variable | Required | Description |
|---|---|---|
REFERRAL_MINTER_PRIVATE_KEY | Yes (mint) | Server wallet with OPERATOR_ROLE — never expose to client |
REFERRAL_REWARDS_ADDRESS | Yes | UUPS proxy address |
REFERRAL_REWARD_TOKEN_ADDRESS | Yes | Mintable ERC20 on target chain |
REFERRAL_REWARD_PERCENT | No | Default 5 |
REFERRAL_CHAIN_ID | No | Default 137 |
REFERRAL_UAH_PER_USD | No | Default 40 |
REFERRAL_REWARD_MODE | No | 0 MINT, 1 TRANSFER |
POLYGON_RPC_URL | Yes (mint) | Server RPC for viem |
REFERRAL_MINTER_ADDRESS | No | Deploy script operator fallback |
DEPLOYER_PRIVATE_KEY | Deploy only | Hardhat deployer |
CRON_SECRET | Prod cron | Bearer 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.
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/Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:
./scripts/install-refcodes-module.sh /path/to/cloneCopies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).
REFERRAL-ONCHAIN-OPS.md (repo root).Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.referral_reward_minted in-app notification to the referrer.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
| Layer | Responsibility |
|---|---|
| Attribution | ?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch) |
| Signup | New users persist users.data.referredBy from ring_ref on first sign-in |
| Checkout | Order stores referralCode, referrerUserId, referrerWallet; review step shows badge |
| Ledger | PostgreSQL referral_rewards — off-chain state + mint tracking |
| ERP rail | Same resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions |
| On-chain | UUPS ReferralRewards contract — idempotent payReferral per order |
orderReference; contract rejects duplicate orderRef hashes.rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.| Route | Access | Purpose |
|---|---|---|
/refcodes | Authenticated | List codes per wallet, copy share URLs, reward history |
/admin/refcodes | Admin | Approve/reject pending fiat rewards, trigger mint |
GET /api/refcodes | Session | JSON: codes + stats for current user |
POST /api/refcodes/mint | Admin | Batch-mint approved rewards |
POST /api/refcodes/track | Public | Visit beacon — increments visits on the refcode |
GET /api/cron/refcodes-mint | Cron (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.
proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat')./api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).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.
// 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 var | Default | Meaning |
|---|---|---|
REFERRAL_REWARD_PERCENT | 5 | Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset |
REFERRAL_COMMISSION_MAX_PERCENT | 50 | Upper bound for all resolved referral rates |
REFERRAL_UAH_PER_USD | 40 | UAH → USD for WayForPay orders |
REFERRAL_CHAIN_ID | 137 | Polygon mainnet |
Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).
| Status | Meaning |
|---|---|
pending_approval | Fiat order paid; awaiting admin |
approved | Ready to mint (credit path skips pending) |
minting | Transaction submitted |
minted | On-chain success; txHash stored |
failed | Mint reverted or RPC error; failureReason set |
rejected | Admin rejected fiat reward |
ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.
| Role / setting | Holder |
|---|---|
DEFAULT_ADMIN_ROLE | Deployer — upgrade, pause, token/mode config |
OPERATOR_ROLE | Server minter wallet — calls payReferral |
rewardMode | 0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER |
function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
external onlyRole(OPERATOR_ROLE) whenNotPausedorderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.
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 polygonSave the proxy address as REFERRAL_REWARDS_ADDRESS.
The token must allow the ReferralRewards proxy to mint:
| Token type | Action |
|---|---|
Ownable mint (e.g. test MockMintableToken) | token.transferOwnership(referralRewardsProxy) |
OpenZeppelin AccessControl | token.grantRole(MINTER_ROLE, referralRewardsProxy) |
| Custom Ring token | Grant 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.
Migration 005_refcodes_schema.sql creates:
| Table | Collection key | Purpose |
|---|---|---|
refcodes | refcodes | Code document; id = code string |
referral_rewards | referral_rewards | Reward 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.
# 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| Variable | Required | Description |
|---|---|---|
REFERRAL_MINTER_PRIVATE_KEY | Yes (mint) | Server wallet with OPERATOR_ROLE — never expose to client |
REFERRAL_REWARDS_ADDRESS | Yes | UUPS proxy address |
REFERRAL_REWARD_TOKEN_ADDRESS | Yes | Mintable ERC20 on target chain |
REFERRAL_REWARD_PERCENT | No | Default 5 |
REFERRAL_CHAIN_ID | No | Default 137 |
REFERRAL_UAH_PER_USD | No | Default 40 |
REFERRAL_REWARD_MODE | No | 0 MINT, 1 TRANSFER |
POLYGON_RPC_URL | Yes (mint) | Server RPC for viem |
REFERRAL_MINTER_ADDRESS | No | Deploy script operator fallback |
DEPLOYER_PRIVATE_KEY | Deploy only | Hardhat deployer |
CRON_SECRET | Prod cron | Bearer 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.
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/Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:
./scripts/install-refcodes-module.sh /path/to/cloneCopies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).
REFERRAL-ONCHAIN-OPS.md (repo root).Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.referral_reward_minted in-app notification to the referrer.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
| Layer | Responsibility |
|---|---|
| Attribution | ?ref=CODE → ring_ref + ring_ref_visible cookies (30 days, first-touch) |
| Signup | New users persist users.data.referredBy from ring_ref on first sign-in |
| Checkout | Order stores referralCode, referrerUserId, referrerWallet; review step shows badge |
| Ledger | PostgreSQL referral_rewards — off-chain state + mint tracking |
| ERP rail | Same resolveReferralCommissionPercent hierarchy deducts vendor commission — see ERP commissions |
| On-chain | UUPS ReferralRewards contract — idempotent payReferral per order |
orderReference; contract rejects duplicate orderRef hashes.rail: 'fiat') needs admin approval; internal credit (rail: 'crypto') mints immediately.| Route | Access | Purpose |
|---|---|---|
/refcodes | Authenticated | List codes per wallet, copy share URLs, reward history |
/admin/refcodes | Admin | Approve/reject pending fiat rewards, trigger mint |
GET /api/refcodes | Session | JSON: codes + stats for current user |
POST /api/refcodes/mint | Admin | Batch-mint approved rewards |
POST /api/refcodes/track | Public | Visit beacon — increments visits on the refcode |
GET /api/cron/refcodes-mint | Cron (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.
proxy.ts reads ?ref= and sets httpOnly ring_ref plus client-readable ring_ref_visible (30 days) if not already set — first-touch wins.signIn event (isNewUser) calls persistSignupReferralAttribution so users.data.referredBy survives beyond the cookie TTL.POST /api/store/orders reads the cookie, resolves the code via RefcodeService.resolveCode, runs resolveOrderReferral guards, and passes attribution into StoreOrdersService.createOrder.handleStoreWayForPayWebhook) → stock + settlements ledger + ReferralRewardService.onOrderPaid (rail: 'fiat')./api/store/payments/credit) → same pipeline with rail: 'crypto' (auto-approved).handleMembershipWayForPayWebhook calls ReferralRewardService.onMembershipPaid when referredBy is set (no prior store order required).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.
// 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 var | Default | Meaning |
|---|---|---|
REFERRAL_REWARD_PERCENT | 5 | Fallback platform % when DEFAULT_COMMISSION_STRUCTURE unset |
REFERRAL_COMMISSION_MAX_PERCENT | 50 | Upper bound for all resolved referral rates |
REFERRAL_UAH_PER_USD | 40 | UAH → USD for WayForPay orders |
REFERRAL_CHAIN_ID | 137 | Polygon mainnet |
Token amount uses the existing price oracle (priceOracleService.convertUsdToRing).
| Status | Meaning |
|---|---|
pending_approval | Fiat order paid; awaiting admin |
approved | Ready to mint (credit path skips pending) |
minting | Transaction submitted |
minted | On-chain success; txHash stored |
failed | Mint reverted or RPC error; failureReason set |
rejected | Admin rejected fiat reward |
ReferralRewards (UUPS, OpenZeppelin 5) in contracts/contracts-src/ReferralRewards.sol.
| Role / setting | Holder |
|---|---|
DEFAULT_ADMIN_ROLE | Deployer — upgrade, pause, token/mode config |
OPERATOR_ROLE | Server minter wallet — calls payReferral |
rewardMode | 0 = MINT (calls IMintableERC20.mint), 1 = TRANSFER |
function payReferral(address refWallet, uint256 amount, bytes32 orderRef)
external onlyRole(OPERATOR_ROLE) whenNotPausedorderRef = keccak256(utf8(orderReference)) — matches PaymentConductor orderReference strings.
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 polygonSave the proxy address as REFERRAL_REWARDS_ADDRESS.
The token must allow the ReferralRewards proxy to mint:
| Token type | Action |
|---|---|
Ownable mint (e.g. test MockMintableToken) | token.transferOwnership(referralRewardsProxy) |
OpenZeppelin AccessControl | token.grantRole(MINTER_ROLE, referralRewardsProxy) |
| Custom Ring token | Grant 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.
Migration 005_refcodes_schema.sql creates:
| Table | Collection key | Purpose |
|---|---|---|
refcodes | refcodes | Code document; id = code string |
referral_rewards | referral_rewards | Reward 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.
# 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| Variable | Required | Description |
|---|---|---|
REFERRAL_MINTER_PRIVATE_KEY | Yes (mint) | Server wallet with OPERATOR_ROLE — never expose to client |
REFERRAL_REWARDS_ADDRESS | Yes | UUPS proxy address |
REFERRAL_REWARD_TOKEN_ADDRESS | Yes | Mintable ERC20 on target chain |
REFERRAL_REWARD_PERCENT | No | Default 5 |
REFERRAL_CHAIN_ID | No | Default 137 |
REFERRAL_UAH_PER_USD | No | Default 40 |
REFERRAL_REWARD_MODE | No | 0 MINT, 1 TRANSFER |
POLYGON_RPC_URL | Yes (mint) | Server RPC for viem |
REFERRAL_MINTER_ADDRESS | No | Deploy script operator fallback |
DEPLOYER_PRIVATE_KEY | Deploy only | Hardhat deployer |
CRON_SECRET | Prod cron | Bearer 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.
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/Use the installer for ring-connect-software, ring-ringdom-org, and other Ring clones:
./scripts/install-refcodes-module.sh /path/to/cloneCopies new files, runs migration when DATABASE_URL is set, and prints shared-file patch anchors (proxy.ts, routes.ts, store hooks, i18n).
REFERRAL-ONCHAIN-OPS.md (repo root).Authorization: Bearer $CRON_SECRET → GET /api/cron/refcodes-mint.referral_reward_minted in-app notification to the referrer.