Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    1. Docs
    2. /Features
    3. /WayForPay Payment Integration

    Updated Jul 16, 20266 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Docs
    2. /Features
    3. /WayForPay Payment Integration

    Updated Jul 16, 20266 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Docs
    2. /Features
    3. /WayForPay Payment Integration

    Updated Jul 16, 20266 min listen

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    WayForPay Payment Integration

    WayForPay is Ring's primary Ukrainian PSP for hosted card checkout (including Apple Pay / Google Pay where enabled by the merchant). All new card flows go through PaymentConductor — store, membership, news promotion, and wallet credit top-up.

    Use Founder / Developer tabs in the docs sidebar to filter this page.

    Env SSOT: WAYFORPAY_MERCHANT_ACCOUNT, WAYFORPAY_SECRET_KEY, WAYFORPAY_MERCHANT_PASSWORD, WAYFORPAY_DOMAIN, WAYFORPAY_API_URL. There is no WAYFORPAY_MERCHANT_ID.

    Money truth: credit and order fulfillment happen only when WayForPay POSTs to serviceUrl with transactionStatus === 'Approved'. The browser returnUrl is UX only — not settlement.

    What WayForPay covers in Ring

    PurposeLive entrySettled by
    store_orderPOST /api/store/payments/wayforpay → PaymentConductor.createCheckouthandlers/store-order.ts
    membership_upgradeinitiateMembershipPayment (card) → createCheckouthandlers/membership-upgrade.ts
    news_promotionNews promotion submithandlers/news-promotion.ts
    wallet_topupWalletConductor.initiateTopUp / initiateCreditTopupPayment → createCheckouthandlers/wallet-topup.ts → creditBalanceService.addFiatUsd

    Recurring membership management uses WayForPay regularApi (lib/payments/subscription/wayforpay-regular-api.ts) with merchantAccount + merchantPassword (not the secret key).

    Operator value

    • Hosted checkout — cards never touch Ring servers; PCI scope stays with WayForPay.
    • No cabinet thank-you page required — Ring sends returnUrl on every payment (e.g. back to /wallet). WayForPay’s own result page is used only if returnUrl is omitted.
    • One public webhook — register https://YOUR_HOST/api/payments/wayforpay/webhook so Approved payments can settle. Localhost cannot receive WayForPay callbacks (use a tunnel or staging for real credit tests).
    • Wallet top-up is live — Add Credit / /wallet/topup Card tab uses PaymentConductor (processor from env: WayForPay or Stripe). PayPal tab on Add Credit sends processor=paypal when PayPal is configured.
    • Membership card path is live — PaymentModal card tab uses PaymentConductor. Membership PayPal is live when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (Subscriptions v1 via SubscriptionConductor) — separate from wallet credit PayPal.

    Credentials checklist

    VariableRequired forNotes
    WAYFORPAY_MERCHANT_ACCOUNTAll checkoutsMerchant login / account id from cabinet

    Environment (verified)

    HPP contract — form POST only

    WayForPay Hosted Payment Page: https://secure.wayforpay.com/pay.

    RuleDetail
    MethodHTML form POST (application/x-www-form-urlencoded)
    GET query URLsInvalid — WayForPay responds Bad Request / This page requires only POST data
    Ring builderlib/payments/wayforpay-hpp.ts → form_post CheckoutRedirect
    Client handofflib/payments/checkout-redirect.ts → followCheckoutResult (unbranded; never import PSP-branded client helpers)

    PaymentConductor returns CreateCheckoutResult.redirect:

    • WayForPay HPP → { mode: 'form_post', url, fields }
    • Stripe Checkout / PayPal approve / WFP invoiceUrl → { mode: 'navigate', url }

    always runs so processors may set either or legacy / .

    Related

    PaymentConductor

    Purposes and rails

    Architecture

    CheckoutRedirect, dispatcher, ledger

    Payment integration (ops)

    Cabinet URLs and checklist

    Wallet

    Add Credit Card / PayPal

    Subscriptions

    WayForPay Payment Integration

    WayForPay is Ring's primary Ukrainian PSP for hosted card checkout (including Apple Pay / Google Pay where enabled by the merchant). All new card flows go through PaymentConductor — store, membership, news promotion, and wallet credit top-up.

    Use Founder / Developer tabs in the docs sidebar to filter this page.

    Env SSOT: WAYFORPAY_MERCHANT_ACCOUNT, WAYFORPAY_SECRET_KEY, WAYFORPAY_MERCHANT_PASSWORD, WAYFORPAY_DOMAIN, WAYFORPAY_API_URL. There is no WAYFORPAY_MERCHANT_ID.

    Money truth: credit and order fulfillment happen only when WayForPay POSTs to serviceUrl with transactionStatus === 'Approved'. The browser returnUrl is UX only — not settlement.

    What WayForPay covers in Ring

    PurposeLive entrySettled by
    store_orderPOST /api/store/payments/wayforpay → PaymentConductor.createCheckouthandlers/store-order.ts
    membership_upgradeinitiateMembershipPayment (card) → createCheckouthandlers/membership-upgrade.ts
    news_promotionNews promotion submithandlers/news-promotion.ts
    wallet_topupWalletConductor.initiateTopUp / initiateCreditTopupPayment → createCheckouthandlers/wallet-topup.ts → creditBalanceService.addFiatUsd

    Recurring membership management uses WayForPay regularApi (lib/payments/subscription/wayforpay-regular-api.ts) with merchantAccount + merchantPassword (not the secret key).

    Operator value

    • Hosted checkout — cards never touch Ring servers; PCI scope stays with WayForPay.
    • No cabinet thank-you page required — Ring sends returnUrl on every payment (e.g. back to /wallet). WayForPay’s own result page is used only if returnUrl is omitted.
    • One public webhook — register https://YOUR_HOST/api/payments/wayforpay/webhook so Approved payments can settle. Localhost cannot receive WayForPay callbacks (use a tunnel or staging for real credit tests).
    • Wallet top-up is live — Add Credit / /wallet/topup Card tab uses PaymentConductor (processor from env: WayForPay or Stripe). PayPal tab on Add Credit sends processor=paypal when PayPal is configured.
    • Membership card path is live — PaymentModal card tab uses PaymentConductor. Membership PayPal is live when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (Subscriptions v1 via SubscriptionConductor) — separate from wallet credit PayPal.

    Credentials checklist

    VariableRequired forNotes
    WAYFORPAY_MERCHANT_ACCOUNTAll checkoutsMerchant login / account id from cabinet

    Environment (verified)

    HPP contract — form POST only

    WayForPay Hosted Payment Page: https://secure.wayforpay.com/pay.

    RuleDetail
    MethodHTML form POST (application/x-www-form-urlencoded)
    GET query URLsInvalid — WayForPay responds Bad Request / This page requires only POST data
    Ring builderlib/payments/wayforpay-hpp.ts → form_post CheckoutRedirect
    Client handofflib/payments/checkout-redirect.ts → followCheckoutResult (unbranded; never import PSP-branded client helpers)

    PaymentConductor returns CreateCheckoutResult.redirect:

    • WayForPay HPP → { mode: 'form_post', url, fields }
    • Stripe Checkout / PayPal approve / WFP invoiceUrl → { mode: 'navigate', url }

    always runs so processors may set either or legacy / .

    Related

    PaymentConductor

    Purposes and rails

    Architecture

    CheckoutRedirect, dispatcher, ledger

    Payment integration (ops)

    Cabinet URLs and checklist

    Wallet

    Add Credit Card / PayPal

    Subscriptions

    WayForPay Payment Integration

    WayForPay is Ring's primary Ukrainian PSP for hosted card checkout (including Apple Pay / Google Pay where enabled by the merchant). All new card flows go through PaymentConductor — store, membership, news promotion, and wallet credit top-up.

    Use Founder / Developer tabs in the docs sidebar to filter this page.

    Env SSOT: WAYFORPAY_MERCHANT_ACCOUNT, WAYFORPAY_SECRET_KEY, WAYFORPAY_MERCHANT_PASSWORD, WAYFORPAY_DOMAIN, WAYFORPAY_API_URL. There is no WAYFORPAY_MERCHANT_ID.

    Money truth: credit and order fulfillment happen only when WayForPay POSTs to serviceUrl with transactionStatus === 'Approved'. The browser returnUrl is UX only — not settlement.

    What WayForPay covers in Ring

    PurposeLive entrySettled by
    store_orderPOST /api/store/payments/wayforpay → PaymentConductor.createCheckouthandlers/store-order.ts
    membership_upgradeinitiateMembershipPayment (card) → createCheckouthandlers/membership-upgrade.ts
    news_promotionNews promotion submithandlers/news-promotion.ts
    wallet_topupWalletConductor.initiateTopUp / initiateCreditTopupPayment → createCheckouthandlers/wallet-topup.ts → creditBalanceService.addFiatUsd

    Recurring membership management uses WayForPay regularApi (lib/payments/subscription/wayforpay-regular-api.ts) with merchantAccount + merchantPassword (not the secret key).

    Operator value

    • Hosted checkout — cards never touch Ring servers; PCI scope stays with WayForPay.
    • No cabinet thank-you page required — Ring sends returnUrl on every payment (e.g. back to /wallet). WayForPay’s own result page is used only if returnUrl is omitted.
    • One public webhook — register https://YOUR_HOST/api/payments/wayforpay/webhook so Approved payments can settle. Localhost cannot receive WayForPay callbacks (use a tunnel or staging for real credit tests).
    • Wallet top-up is live — Add Credit / /wallet/topup Card tab uses PaymentConductor (processor from env: WayForPay or Stripe). PayPal tab on Add Credit sends processor=paypal when PayPal is configured.
    • Membership card path is live — PaymentModal card tab uses PaymentConductor. Membership PayPal is live when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (Subscriptions v1 via SubscriptionConductor) — separate from wallet credit PayPal.

    Credentials checklist

    VariableRequired forNotes
    WAYFORPAY_MERCHANT_ACCOUNTAll checkoutsMerchant login / account id from cabinet

    Environment (verified)

    HPP contract — form POST only

    WayForPay Hosted Payment Page: https://secure.wayforpay.com/pay.

    RuleDetail
    MethodHTML form POST (application/x-www-form-urlencoded)
    GET query URLsInvalid — WayForPay responds Bad Request / This page requires only POST data
    Ring builderlib/payments/wayforpay-hpp.ts → form_post CheckoutRedirect
    Client handofflib/payments/checkout-redirect.ts → followCheckoutResult (unbranded; never import PSP-branded client helpers)

    PaymentConductor returns CreateCheckoutResult.redirect:

    • WayForPay HPP → { mode: 'form_post', url, fields }
    • Stripe Checkout / PayPal approve / WFP invoiceUrl → { mode: 'navigate', url }

    always runs so processors may set either or legacy / .

    Related

    PaymentConductor

    Purposes and rails

    Architecture

    CheckoutRedirect, dispatcher, ledger

    Payment integration (ops)

    Cabinet URLs and checklist

    Wallet

    Add Credit Card / PayPal

    Subscriptions

    WAYFORPAY_SECRET_KEY
    HMAC signatures
    Server-only — never NEXT_PUBLIC_*
    WAYFORPAY_MERCHANT_PASSWORDRecurring / regularApiRequired for subscription lifecycle
    WAYFORPAY_DOMAINMerchant domain matchThis product: ring-platform.org
    WAYFORPAY_API_URLAPI baseDefault https://api.wayforpay.com/api

    Templates: env.local.template, docker.env.template, docker-compose.template.yml.

    Cabinet service URL

    Use your clone host in white-label deployments. Do not register legacy /callback, /success, or /failure paths as the settlement endpoint.

    After a member pays with card

    1. Browser returns to Ring (returnUrl) — they see the wallet/store page.
    2. WayForPay’s servers POST the result to your webhook.
    3. Only then is credit added or the order marked paid.

    If the member is back on /wallet but credit is missing, the webhook did not land (wrong host, localhost, or HMAC failure) — check payment_transactions and server logs.

    createCheckout
    normalizeCheckoutResult
    redirect
    paymentUrl
    paymentFields

    returnUrl vs serviceUrl

    FieldWho sets itRoleTrust for money?
    returnUrlRing per request (WalletConductor.initiateTopUp, store/membership contexts)Browser land-back after payNo
    serviceUrlRing via getWebhookUrl('wayforpay')Server POST from WayForPayYes — settlement

    Wallet credit settlement: handlers/wallet-topup.ts requires transactionStatus === 'Approved', then markPaid + creditBalanceService.addFiatUsd.

    Module map

    ConcernPath
    Processor (all purposes)lib/payments/processors/wayforpay.processor.ts
    HPP sign + fieldslib/payments/wayforpay-hpp.ts, wayforpay-hpp-types.ts
    Unbranded browser handofflib/payments/checkout-redirect.ts
    Signature verifylib/payments/processors/wayforpay-verify.ts
    Store form helperlib/payments/wayforpay-store-service.ts (initiateStorePayment — called from processor only)
    Webhook dispatcherlib/payments/conductor/webhook-dispatcher.ts
    Handlerslib/payments/conductor/handlers/*.ts
    regularApilib/payments/subscription/wayforpay-regular-api.ts
    Subscription providerlib/payments/subscription/providers/wayforpay-subscription.ts
    Canonical webhookapp/api/payments/wayforpay/webhook/route.ts

    Checkout sequence

    1. 1

      Caller invokes PaymentConductor.createCheckout({ purpose, userId, amount, currency, returnUrl, ... }). Conductor resolves processor (metadata.processor or getPaymentProvider(purpose)).

    2. 2

      wayforpay.processor.ts builds orderReference, writes createPending, builds HPP fields (returnUrl + serviceUrl), returns redirect: { mode: 'form_post', ... }. Conductor runs normalizeCheckoutResult.

    3. 3

      UI calls followCheckoutResult — hidden form POST to secure.wayforpay.com/pay (not window.location with a query string).

    4. 4

      User pays; browser may hit returnUrl. WayForPay POSTs to /api/payments/wayforpay/webhook.

    5. 5

      dispatchWayForPayWebhook verifies HMAC, parses purpose from orderReference, marks paid, runs the purpose handler (e.g. wallet credit).

    Order reference prefixes

    PurposePrefix pattern
    store_orderstore_{orderId}_{ts}
    membership_upgrademembership_{userId}_{ts} (legacy ring_ still parsed)
    news_promotionnews-promo-{base64url(articleId)}-{ts}
    wallet_topupwallettopup_{userId}_{ts}

    Wallet top-up

    UI: features/wallet/components/credit-add-fs-modal.tsx (Card | PayPal tabs) and /wallet/topup.

    Membership card

    initiateMembershipPayment with card / wayforpay / stripe → createCheckout({ purpose: 'membership_upgrade' }). UI: components/membership/payment-modal.tsx — native-token + card live; PayPal tab when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true → POST /api/membership/payment/paypal.

    Store

    POST /api/store/payments/wayforpay → PaymentConductor.createCheckout({ purpose: 'store_order' }). No route-level bypass of the ledger. Client uses followCheckoutResult with conductor redirect.

    regularApi (subscriptions)

    WAYFORPAY_MERCHANT_PASSWORD + merchantAccount authenticate JSON calls to https://api.wayforpay.com/regularApi (STATUS, CHANGE, SUSPEND, RESUME, REMOVE). See wayforpay-regular-api.ts.

    Security

    • Generate and verify HMAC server-side only with WAYFORPAY_SECRET_KEY.
    • Never embed production secrets in MDX, client bundles, or git.
    • Idempotent ledger: duplicate Approved webhooks return success without double-crediting (markPaid / handler guards).
    • Never treat returnUrl query params as payment confirmation.

    Smoke / ops

    • Staging: migration 004 applied → store + membership card + wallet top-up → inspect payment_transactions.
    • Webhook must be public HTTPS — localhost serviceUrl will not settle credit.
    • Admin: GET /api/admin/users/[id]/payments (UI: admin-user-detail-sheet.tsx Payments tab).

    SubscriptionConductor + WayForPay recurring

    text
    
    https://YOUR_HOST/api/payments/wayforpay/webhook
    WAYFORPAY_SECRET_KEY
    HMAC signatures
    Server-only — never NEXT_PUBLIC_*
    WAYFORPAY_MERCHANT_PASSWORDRecurring / regularApiRequired for subscription lifecycle
    WAYFORPAY_DOMAINMerchant domain matchThis product: ring-platform.org
    WAYFORPAY_API_URLAPI baseDefault https://api.wayforpay.com/api

    Templates: env.local.template, docker.env.template, docker-compose.template.yml.

    Cabinet service URL

    Use your clone host in white-label deployments. Do not register legacy /callback, /success, or /failure paths as the settlement endpoint.

    After a member pays with card

    1. Browser returns to Ring (returnUrl) — they see the wallet/store page.
    2. WayForPay’s servers POST the result to your webhook.
    3. Only then is credit added or the order marked paid.

    If the member is back on /wallet but credit is missing, the webhook did not land (wrong host, localhost, or HMAC failure) — check payment_transactions and server logs.

    createCheckout
    normalizeCheckoutResult
    redirect
    paymentUrl
    paymentFields

    returnUrl vs serviceUrl

    FieldWho sets itRoleTrust for money?
    returnUrlRing per request (WalletConductor.initiateTopUp, store/membership contexts)Browser land-back after payNo
    serviceUrlRing via getWebhookUrl('wayforpay')Server POST from WayForPayYes — settlement

    Wallet credit settlement: handlers/wallet-topup.ts requires transactionStatus === 'Approved', then markPaid + creditBalanceService.addFiatUsd.

    Module map

    ConcernPath
    Processor (all purposes)lib/payments/processors/wayforpay.processor.ts
    HPP sign + fieldslib/payments/wayforpay-hpp.ts, wayforpay-hpp-types.ts
    Unbranded browser handofflib/payments/checkout-redirect.ts
    Signature verifylib/payments/processors/wayforpay-verify.ts
    Store form helperlib/payments/wayforpay-store-service.ts (initiateStorePayment — called from processor only)
    Webhook dispatcherlib/payments/conductor/webhook-dispatcher.ts
    Handlerslib/payments/conductor/handlers/*.ts
    regularApilib/payments/subscription/wayforpay-regular-api.ts
    Subscription providerlib/payments/subscription/providers/wayforpay-subscription.ts
    Canonical webhookapp/api/payments/wayforpay/webhook/route.ts

    Checkout sequence

    1. 1

      Caller invokes PaymentConductor.createCheckout({ purpose, userId, amount, currency, returnUrl, ... }). Conductor resolves processor (metadata.processor or getPaymentProvider(purpose)).

    2. 2

      wayforpay.processor.ts builds orderReference, writes createPending, builds HPP fields (returnUrl + serviceUrl), returns redirect: { mode: 'form_post', ... }. Conductor runs normalizeCheckoutResult.

    3. 3

      UI calls followCheckoutResult — hidden form POST to secure.wayforpay.com/pay (not window.location with a query string).

    4. 4

      User pays; browser may hit returnUrl. WayForPay POSTs to /api/payments/wayforpay/webhook.

    5. 5

      dispatchWayForPayWebhook verifies HMAC, parses purpose from orderReference, marks paid, runs the purpose handler (e.g. wallet credit).

    Order reference prefixes

    PurposePrefix pattern
    store_orderstore_{orderId}_{ts}
    membership_upgrademembership_{userId}_{ts} (legacy ring_ still parsed)
    news_promotionnews-promo-{base64url(articleId)}-{ts}
    wallet_topupwallettopup_{userId}_{ts}

    Wallet top-up

    UI: features/wallet/components/credit-add-fs-modal.tsx (Card | PayPal tabs) and /wallet/topup.

    Membership card

    initiateMembershipPayment with card / wayforpay / stripe → createCheckout({ purpose: 'membership_upgrade' }). UI: components/membership/payment-modal.tsx — native-token + card live; PayPal tab when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true → POST /api/membership/payment/paypal.

    Store

    POST /api/store/payments/wayforpay → PaymentConductor.createCheckout({ purpose: 'store_order' }). No route-level bypass of the ledger. Client uses followCheckoutResult with conductor redirect.

    regularApi (subscriptions)

    WAYFORPAY_MERCHANT_PASSWORD + merchantAccount authenticate JSON calls to https://api.wayforpay.com/regularApi (STATUS, CHANGE, SUSPEND, RESUME, REMOVE). See wayforpay-regular-api.ts.

    Security

    • Generate and verify HMAC server-side only with WAYFORPAY_SECRET_KEY.
    • Never embed production secrets in MDX, client bundles, or git.
    • Idempotent ledger: duplicate Approved webhooks return success without double-crediting (markPaid / handler guards).
    • Never treat returnUrl query params as payment confirmation.

    Smoke / ops

    • Staging: migration 004 applied → store + membership card + wallet top-up → inspect payment_transactions.
    • Webhook must be public HTTPS — localhost serviceUrl will not settle credit.
    • Admin: GET /api/admin/users/[id]/payments (UI: admin-user-detail-sheet.tsx Payments tab).

    SubscriptionConductor + WayForPay recurring

    text
    
    https://YOUR_HOST/api/payments/wayforpay/webhook
    WAYFORPAY_SECRET_KEY
    HMAC signatures
    Server-only — never NEXT_PUBLIC_*
    WAYFORPAY_MERCHANT_PASSWORDRecurring / regularApiRequired for subscription lifecycle
    WAYFORPAY_DOMAINMerchant domain matchThis product: ring-platform.org
    WAYFORPAY_API_URLAPI baseDefault https://api.wayforpay.com/api

    Templates: env.local.template, docker.env.template, docker-compose.template.yml.

    Cabinet service URL

    Use your clone host in white-label deployments. Do not register legacy /callback, /success, or /failure paths as the settlement endpoint.

    After a member pays with card

    1. Browser returns to Ring (returnUrl) — they see the wallet/store page.
    2. WayForPay’s servers POST the result to your webhook.
    3. Only then is credit added or the order marked paid.

    If the member is back on /wallet but credit is missing, the webhook did not land (wrong host, localhost, or HMAC failure) — check payment_transactions and server logs.

    createCheckout
    normalizeCheckoutResult
    redirect
    paymentUrl
    paymentFields

    returnUrl vs serviceUrl

    FieldWho sets itRoleTrust for money?
    returnUrlRing per request (WalletConductor.initiateTopUp, store/membership contexts)Browser land-back after payNo
    serviceUrlRing via getWebhookUrl('wayforpay')Server POST from WayForPayYes — settlement

    Wallet credit settlement: handlers/wallet-topup.ts requires transactionStatus === 'Approved', then markPaid + creditBalanceService.addFiatUsd.

    Module map

    ConcernPath
    Processor (all purposes)lib/payments/processors/wayforpay.processor.ts
    HPP sign + fieldslib/payments/wayforpay-hpp.ts, wayforpay-hpp-types.ts
    Unbranded browser handofflib/payments/checkout-redirect.ts
    Signature verifylib/payments/processors/wayforpay-verify.ts
    Store form helperlib/payments/wayforpay-store-service.ts (initiateStorePayment — called from processor only)
    Webhook dispatcherlib/payments/conductor/webhook-dispatcher.ts
    Handlerslib/payments/conductor/handlers/*.ts
    regularApilib/payments/subscription/wayforpay-regular-api.ts
    Subscription providerlib/payments/subscription/providers/wayforpay-subscription.ts
    Canonical webhookapp/api/payments/wayforpay/webhook/route.ts

    Checkout sequence

    1. 1

      Caller invokes PaymentConductor.createCheckout({ purpose, userId, amount, currency, returnUrl, ... }). Conductor resolves processor (metadata.processor or getPaymentProvider(purpose)).

    2. 2

      wayforpay.processor.ts builds orderReference, writes createPending, builds HPP fields (returnUrl + serviceUrl), returns redirect: { mode: 'form_post', ... }. Conductor runs normalizeCheckoutResult.

    3. 3

      UI calls followCheckoutResult — hidden form POST to secure.wayforpay.com/pay (not window.location with a query string).

    4. 4

      User pays; browser may hit returnUrl. WayForPay POSTs to /api/payments/wayforpay/webhook.

    5. 5

      dispatchWayForPayWebhook verifies HMAC, parses purpose from orderReference, marks paid, runs the purpose handler (e.g. wallet credit).

    Order reference prefixes

    PurposePrefix pattern
    store_orderstore_{orderId}_{ts}
    membership_upgrademembership_{userId}_{ts} (legacy ring_ still parsed)
    news_promotionnews-promo-{base64url(articleId)}-{ts}
    wallet_topupwallettopup_{userId}_{ts}

    Wallet top-up

    UI: features/wallet/components/credit-add-fs-modal.tsx (Card | PayPal tabs) and /wallet/topup.

    Membership card

    initiateMembershipPayment with card / wayforpay / stripe → createCheckout({ purpose: 'membership_upgrade' }). UI: components/membership/payment-modal.tsx — native-token + card live; PayPal tab when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true → POST /api/membership/payment/paypal.

    Store

    POST /api/store/payments/wayforpay → PaymentConductor.createCheckout({ purpose: 'store_order' }). No route-level bypass of the ledger. Client uses followCheckoutResult with conductor redirect.

    regularApi (subscriptions)

    WAYFORPAY_MERCHANT_PASSWORD + merchantAccount authenticate JSON calls to https://api.wayforpay.com/regularApi (STATUS, CHANGE, SUSPEND, RESUME, REMOVE). See wayforpay-regular-api.ts.

    Security

    • Generate and verify HMAC server-side only with WAYFORPAY_SECRET_KEY.
    • Never embed production secrets in MDX, client bundles, or git.
    • Idempotent ledger: duplicate Approved webhooks return success without double-crediting (markPaid / handler guards).
    • Never treat returnUrl query params as payment confirmation.

    Smoke / ops

    • Staging: migration 004 applied → store + membership card + wallet top-up → inspect payment_transactions.
    • Webhook must be public HTTPS — localhost serviceUrl will not settle credit.
    • Admin: GET /api/admin/users/[id]/payments (UI: admin-user-detail-sheet.tsx Payments tab).

    SubscriptionConductor + WayForPay recurring

    text
    
    https://YOUR_HOST/api/payments/wayforpay/webhook