Concepts, value, and typical clone scenarios — less code.
Concepts, value, and typical clone scenarios — less code.
Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
PaymentConductor is Ring Platform's config-driven payment layer. One ledger (payment_transactions) and one webhook dispatcher serve store checkout, membership upgrades, news promotion, and wallet credit top-up.
Browser UIs never talk to a PSP by brand for redirect — they follow Conductor CheckoutRedirect via lib/payments/checkout-redirect.ts (followCheckoutResult). Deep types: PaymentConductor architecture.
Use Founder / Developer tabs in the docs sidebar to filter this page. Founders see configuration and business value; developers see modules, purposes, and webhook flows.
| Purpose | Handler | Typical processor | Entry |
|---|---|---|---|
store_order | handlers/store-order.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, credit, native token, or PayPal | POST /api/store/payments/{wayforpay|stripe|token|credit|paypal|card} → PaymentConductor.createCheckout |
membership_upgrade | handlers/membership-upgrade.ts (+ Stripe / PayPal capture + Subscriptions lifecycle) | WayForPay, Stripe, or PayPal | Card / native via initiateMembershipPayment; PayPal via POST /api/membership/payment/paypal → SubscriptionConductor |
news_promotion | handlers/news-promotion.ts | WayForPay or Stripe | News promotion submit |
wallet_topup | handlers/wallet-topup.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, or PayPal | WalletConductor initiateTopUp → createCheckout |
native_token_onramp | handlers/native-token-onramp.ts (+ Stripe: native-token-onramp-stripe.ts) | WayForPay / Stripe (PayPal unsupported for onramp) | WalletConductor initiateNativeOnramp (confidential+) |
wallet_topup credits the fiat credit ledger (1:1 USD points) via creditBalanceService.addFiatUsd — it does not buy on-chain RING. To spend native RING directly, use the native_token rail on store_order. To convert credit points → native RING, use the Token Desk.
ring-config.json; override per purpose via env. No code changes for PSP swaps.PAYMENT_STORE_ALLOW_TOKEN=true, zero gateway fee).membership_upgrade and wallet_topup rows from the ledger (GET /api/admin/users/[id]/payments).NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (plus PAYPAL_* + gateways.paypal.enabled). Recurring PayPal membership uses Subscriptions v1 — see SubscriptionConductor. Wallet Add Credit PayPal remains processor=paypal on wallet_topup.paypal → POST /api/store/payments/paypal (Orders v2) when the same public flag is on.redirect (navigate or form_post). WayForPay HPP must not be opened as a GET query URL.ring-config.json)| Gateway | Fee % |
|---|
High-level payment integration — store, membership, news, wallet top-up.
Env SSOT, HMAC, regularApi password, orderReference prefixes.
Types, ledger, dispatcher sequences, API routes.
PayPal Subscriptions v1, ledger, manage page, cancel/renew.
PaymentConductor is Ring Platform's config-driven payment layer. One ledger (payment_transactions) and one webhook dispatcher serve store checkout, membership upgrades, news promotion, and wallet credit top-up.
Browser UIs never talk to a PSP by brand for redirect — they follow Conductor CheckoutRedirect via lib/payments/checkout-redirect.ts (followCheckoutResult). Deep types: PaymentConductor architecture.
Use Founder / Developer tabs in the docs sidebar to filter this page. Founders see configuration and business value; developers see modules, purposes, and webhook flows.
| Purpose | Handler | Typical processor | Entry |
|---|---|---|---|
store_order | handlers/store-order.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, credit, native token, or PayPal | POST /api/store/payments/{wayforpay|stripe|token|credit|paypal|card} → PaymentConductor.createCheckout |
membership_upgrade | handlers/membership-upgrade.ts (+ Stripe / PayPal capture + Subscriptions lifecycle) | WayForPay, Stripe, or PayPal | Card / native via initiateMembershipPayment; PayPal via POST /api/membership/payment/paypal → SubscriptionConductor |
news_promotion | handlers/news-promotion.ts | WayForPay or Stripe | News promotion submit |
wallet_topup | handlers/wallet-topup.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, or PayPal | WalletConductor initiateTopUp → createCheckout |
native_token_onramp | handlers/native-token-onramp.ts (+ Stripe: native-token-onramp-stripe.ts) | WayForPay / Stripe (PayPal unsupported for onramp) | WalletConductor initiateNativeOnramp (confidential+) |
wallet_topup credits the fiat credit ledger (1:1 USD points) via creditBalanceService.addFiatUsd — it does not buy on-chain RING. To spend native RING directly, use the native_token rail on store_order. To convert credit points → native RING, use the Token Desk.
ring-config.json; override per purpose via env. No code changes for PSP swaps.PAYMENT_STORE_ALLOW_TOKEN=true, zero gateway fee).membership_upgrade and wallet_topup rows from the ledger (GET /api/admin/users/[id]/payments).NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (plus PAYPAL_* + gateways.paypal.enabled). Recurring PayPal membership uses Subscriptions v1 — see SubscriptionConductor. Wallet Add Credit PayPal remains processor=paypal on wallet_topup.paypal → POST /api/store/payments/paypal (Orders v2) when the same public flag is on.redirect (navigate or form_post). WayForPay HPP must not be opened as a GET query URL.ring-config.json)| Gateway | Fee % |
|---|
High-level payment integration — store, membership, news, wallet top-up.
Env SSOT, HMAC, regularApi password, orderReference prefixes.
Types, ledger, dispatcher sequences, API routes.
PayPal Subscriptions v1, ledger, manage page, cancel/renew.
PaymentConductor is Ring Platform's config-driven payment layer. One ledger (payment_transactions) and one webhook dispatcher serve store checkout, membership upgrades, news promotion, and wallet credit top-up.
Browser UIs never talk to a PSP by brand for redirect — they follow Conductor CheckoutRedirect via lib/payments/checkout-redirect.ts (followCheckoutResult). Deep types: PaymentConductor architecture.
Use Founder / Developer tabs in the docs sidebar to filter this page. Founders see configuration and business value; developers see modules, purposes, and webhook flows.
| Purpose | Handler | Typical processor | Entry |
|---|---|---|---|
store_order | handlers/store-order.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, credit, native token, or PayPal | POST /api/store/payments/{wayforpay|stripe|token|credit|paypal|card} → PaymentConductor.createCheckout |
membership_upgrade | handlers/membership-upgrade.ts (+ Stripe / PayPal capture + Subscriptions lifecycle) | WayForPay, Stripe, or PayPal | Card / native via initiateMembershipPayment; PayPal via POST /api/membership/payment/paypal → SubscriptionConductor |
news_promotion | handlers/news-promotion.ts | WayForPay or Stripe | News promotion submit |
wallet_topup | handlers/wallet-topup.ts (+ Stripe / PayPal capture handlers) | WayForPay, Stripe, or PayPal | WalletConductor initiateTopUp → createCheckout |
native_token_onramp | handlers/native-token-onramp.ts (+ Stripe: native-token-onramp-stripe.ts) | WayForPay / Stripe (PayPal unsupported for onramp) | WalletConductor initiateNativeOnramp (confidential+) |
wallet_topup credits the fiat credit ledger (1:1 USD points) via creditBalanceService.addFiatUsd — it does not buy on-chain RING. To spend native RING directly, use the native_token rail on store_order. To convert credit points → native RING, use the Token Desk.
ring-config.json; override per purpose via env. No code changes for PSP swaps.PAYMENT_STORE_ALLOW_TOKEN=true, zero gateway fee).membership_upgrade and wallet_topup rows from the ledger (GET /api/admin/users/[id]/payments).NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true (plus PAYPAL_* + gateways.paypal.enabled). Recurring PayPal membership uses Subscriptions v1 — see SubscriptionConductor. Wallet Add Credit PayPal remains processor=paypal on wallet_topup.paypal → POST /api/store/payments/paypal (Orders v2) when the same public flag is on.redirect (navigate or form_post). WayForPay HPP must not be opened as a GET query URL.ring-config.json)| Gateway | Fee % |
|---|
High-level payment integration — store, membership, news, wallet top-up.
Env SSOT, HMAC, regularApi password, orderReference prefixes.
Types, ledger, dispatcher sequences, API routes.
PayPal Subscriptions v1, ledger, manage page, cancel/renew.
| Fixed Fee |
|---|
| Currency |
|---|
| Status |
|---|
| WayForPay | 2.5% | — | UAH | Live |
| Stripe | 2.9% | $0.30 | USD | Live |
| Credit Balance | 0% | — | USD | Live |
| RING Token | 0% | — | RING | Live |
| PayPal | 2.9% | $0.30 | USD | Live (Orders + membership Subscriptions) |
There is no WAYFORPAY_MERCHANT_ID. Use WAYFORPAY_MERCHANT_ACCOUNT. WAYFORPAY_MERCHANT_PASSWORD is required for recurring / regularApi subscription management.
Blank PAYMENT_*_PROCESSOR falls back to PAYMENT_DEFAULT_PROCESSOR (default wayforpay). Set it to stripe for USD-first clones.
lib/payments/processors/wayforpay.processor.ts |
Per-purpose checkout; HPP via wayforpay-hpp.ts |
| Stripe | lib/payments/processors/stripe.processor.ts | Stripe Checkout sessions (navigate) |
| Internal credit | lib/payments/processors/internal-credit.processor.ts | Credit balance deduction |
| Native token | lib/payments/processors/native-token.processor.ts | Synchronous on-chain RING → treasury |
| PayPal | lib/payments/processors/paypal.processor.ts | Orders v2 approve URL (navigate); store + wallet_topup + membership one-shot |
| Webhook dispatcher | lib/payments/conductor/webhook-dispatcher.ts | Orders capture by purpose + PayPal Subscriptions lifecycle branch |
| Purpose handlers | lib/payments/conductor/handlers/*.ts | Fulfillment after paid (incl. membership-paypal-subscription.ts) |
PaymentProcessorId (conductor/types.ts): wayforpay | stripe | internal-credit | native-token | paypal.
| Rail | Description | Env gate |
|---|---|---|
merchant_redirect | WayForPay / Stripe hosted checkout | Default |
internal_credit | Wallet credit balance (fiat points) | PAYMENT_STORE_ALLOW_CREDIT |
native_token | RING token on-chain to treasury | PAYMENT_STORE_ALLOW_TOKEN |
| Endpoint | Provider |
|---|---|
/api/payments/wayforpay/webhook | WayForPay (HMAC) |
/api/payments/stripe/webhook | Stripe (STRIPE_WEBHOOK_SECRET) |
/api/payments/paypal/webhook | PayPal (transmission signature + PAYPAL_WEBHOOK_ID) |
internal_credit and native_token settle synchronously inside createCheckout (no external webhook) — the processor debits credit / transfers on-chain and marks the ledger row paid before returning.
| Route | Rail | Notes |
|---|---|---|
POST /api/store/payments/wayforpay | merchant_redirect | createCheckout({ purpose: 'store_order' }); initiateStorePayment is an internal helper inside wayforpay.processor.ts, not a ledger bypass |
POST /api/store/payments/stripe | merchant_redirect | Stripe Checkout session |
POST /api/store/payments/token | native_token | Requires PAYMENT_STORE_ALLOW_TOKEN=true; isRailEnabled('store_order', 'native_token') gate |
POST /api/store/payments/credit | internal_credit | Credit balance deduction |
POST /api/store/payments/paypal | merchant_redirect | Orders v2; requires PayPal credentials + gateways.paypal.enabled + public flag |
POST /api/store/payments/card | merchant_redirect | Alias of WayForPay card path |
createNativeTokenCheckout (native-token.processor.ts) resolves fiat → token via the oracle (nativeOut = amount / ringPerUsd, or explicit metadata.tokenAmount), checks balance, then transferNativeTokenForUser → treasury and markPaid. Fails closed with NATIVE_TOKEN_RAIL_DISABLED, TREASURY_NOT_CONFIGURED, or INSUFFICIENT_TOKEN_BALANCE.
initiateCreditTopupPayment in app/_actions/wallet.ts → purpose: 'wallet_topup' → createWalletTopupWayForPay (or Stripe) → webhook handlers/wallet-topup.ts / wallet-topup-stripe.ts credits fiat USD points via creditBalanceService.addFiatUsd (1:1). This is distinct from the native_token rail: card top-up never mints on-chain RING.
POST /api/membership/payment/paypal → SubscriptionConductor provider paypal:
auto_subscribe) — PayPal Subscriptions v1; ledger pending → BILLING.SUBSCRIPTION.ACTIVATED → active; cancel uses paypal_subscription_id.recordPaidSubscription (never re-enters provider create).PaymentModal PayPal tab: components/membership/payment-modal.tsx when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true. Member manage page: /membership/manage — see SubscriptionConductor.
Wallet Add Credit PayPal still uses Orders on wallet_topup when credentials are configured.
Apply data/migrations/004_payment_transactions.sql before production. Ledger rows use the DatabaseService result contract { success, data, error }.
Credit top-up via WayForPay / Stripe / PayPal (wallet_topup).
{
"payment": {
"cardPaymentProcessor": "wayforpay",
"supportedMethods": ["wayforpay", "credit_balance", "native_token"],
"gateways": {
"wayforpay": { "enabled": true, "feePercent": 2.5, "currency": "UAH" },
"stripe": { "enabled": false, "feePercent": 2.9, "feeFixedCents": 30, "currency": "USD" }
}
}
}
PAYMENT_STORE_PROCESSOR=stripe
PAYMENT_MEMBERSHIP_PROCESSOR=stripe
PAYMENT_NEWS_PROCESSOR=wayforpay
PAYMENT_WALLET_TOPUP_PROCESSOR=wayforpay
PAYMENT_STORE_ALLOW_CREDIT=true # spend credit points at store checkout
PAYMENT_STORE_ALLOW_TOKEN=false # spend native token at store checkout
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_TOKEN=false
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=false
# PAYPAL_MODE=sandbox
# PAYPAL_CLIENT_ID=
# PAYPAL_CLIENT_SECRET=
# PAYPAL_WEBHOOK_ID=
CONFIDENTIAL_TOKEN_ONRAMP=false # confidential+ BuyNativeViaCard (native_token_onramp)
NEXT_PUBLIC_CONFIDENTIAL_TOKEN_ONRAMP=false
# Token Desk (credit→RING) is subscriber+ — no confidential env gate| Fixed Fee |
|---|
| Currency |
|---|
| Status |
|---|
| WayForPay | 2.5% | — | UAH | Live |
| Stripe | 2.9% | $0.30 | USD | Live |
| Credit Balance | 0% | — | USD | Live |
| RING Token | 0% | — | RING | Live |
| PayPal | 2.9% | $0.30 | USD | Live (Orders + membership Subscriptions) |
There is no WAYFORPAY_MERCHANT_ID. Use WAYFORPAY_MERCHANT_ACCOUNT. WAYFORPAY_MERCHANT_PASSWORD is required for recurring / regularApi subscription management.
Blank PAYMENT_*_PROCESSOR falls back to PAYMENT_DEFAULT_PROCESSOR (default wayforpay). Set it to stripe for USD-first clones.
lib/payments/processors/wayforpay.processor.ts |
Per-purpose checkout; HPP via wayforpay-hpp.ts |
| Stripe | lib/payments/processors/stripe.processor.ts | Stripe Checkout sessions (navigate) |
| Internal credit | lib/payments/processors/internal-credit.processor.ts | Credit balance deduction |
| Native token | lib/payments/processors/native-token.processor.ts | Synchronous on-chain RING → treasury |
| PayPal | lib/payments/processors/paypal.processor.ts | Orders v2 approve URL (navigate); store + wallet_topup + membership one-shot |
| Webhook dispatcher | lib/payments/conductor/webhook-dispatcher.ts | Orders capture by purpose + PayPal Subscriptions lifecycle branch |
| Purpose handlers | lib/payments/conductor/handlers/*.ts | Fulfillment after paid (incl. membership-paypal-subscription.ts) |
PaymentProcessorId (conductor/types.ts): wayforpay | stripe | internal-credit | native-token | paypal.
| Rail | Description | Env gate |
|---|---|---|
merchant_redirect | WayForPay / Stripe hosted checkout | Default |
internal_credit | Wallet credit balance (fiat points) | PAYMENT_STORE_ALLOW_CREDIT |
native_token | RING token on-chain to treasury | PAYMENT_STORE_ALLOW_TOKEN |
| Endpoint | Provider |
|---|---|
/api/payments/wayforpay/webhook | WayForPay (HMAC) |
/api/payments/stripe/webhook | Stripe (STRIPE_WEBHOOK_SECRET) |
/api/payments/paypal/webhook | PayPal (transmission signature + PAYPAL_WEBHOOK_ID) |
internal_credit and native_token settle synchronously inside createCheckout (no external webhook) — the processor debits credit / transfers on-chain and marks the ledger row paid before returning.
| Route | Rail | Notes |
|---|---|---|
POST /api/store/payments/wayforpay | merchant_redirect | createCheckout({ purpose: 'store_order' }); initiateStorePayment is an internal helper inside wayforpay.processor.ts, not a ledger bypass |
POST /api/store/payments/stripe | merchant_redirect | Stripe Checkout session |
POST /api/store/payments/token | native_token | Requires PAYMENT_STORE_ALLOW_TOKEN=true; isRailEnabled('store_order', 'native_token') gate |
POST /api/store/payments/credit | internal_credit | Credit balance deduction |
POST /api/store/payments/paypal | merchant_redirect | Orders v2; requires PayPal credentials + gateways.paypal.enabled + public flag |
POST /api/store/payments/card | merchant_redirect | Alias of WayForPay card path |
createNativeTokenCheckout (native-token.processor.ts) resolves fiat → token via the oracle (nativeOut = amount / ringPerUsd, or explicit metadata.tokenAmount), checks balance, then transferNativeTokenForUser → treasury and markPaid. Fails closed with NATIVE_TOKEN_RAIL_DISABLED, TREASURY_NOT_CONFIGURED, or INSUFFICIENT_TOKEN_BALANCE.
initiateCreditTopupPayment in app/_actions/wallet.ts → purpose: 'wallet_topup' → createWalletTopupWayForPay (or Stripe) → webhook handlers/wallet-topup.ts / wallet-topup-stripe.ts credits fiat USD points via creditBalanceService.addFiatUsd (1:1). This is distinct from the native_token rail: card top-up never mints on-chain RING.
POST /api/membership/payment/paypal → SubscriptionConductor provider paypal:
auto_subscribe) — PayPal Subscriptions v1; ledger pending → BILLING.SUBSCRIPTION.ACTIVATED → active; cancel uses paypal_subscription_id.recordPaidSubscription (never re-enters provider create).PaymentModal PayPal tab: components/membership/payment-modal.tsx when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true. Member manage page: /membership/manage — see SubscriptionConductor.
Wallet Add Credit PayPal still uses Orders on wallet_topup when credentials are configured.
Apply data/migrations/004_payment_transactions.sql before production. Ledger rows use the DatabaseService result contract { success, data, error }.
Credit top-up via WayForPay / Stripe / PayPal (wallet_topup).
{
"payment": {
"cardPaymentProcessor": "wayforpay",
"supportedMethods": ["wayforpay", "credit_balance", "native_token"],
"gateways": {
"wayforpay": { "enabled": true, "feePercent": 2.5, "currency": "UAH" },
"stripe": { "enabled": false, "feePercent": 2.9, "feeFixedCents": 30, "currency": "USD" }
}
}
}
PAYMENT_STORE_PROCESSOR=stripe
PAYMENT_MEMBERSHIP_PROCESSOR=stripe
PAYMENT_NEWS_PROCESSOR=wayforpay
PAYMENT_WALLET_TOPUP_PROCESSOR=wayforpay
PAYMENT_STORE_ALLOW_CREDIT=true # spend credit points at store checkout
PAYMENT_STORE_ALLOW_TOKEN=false # spend native token at store checkout
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_TOKEN=false
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=false
# PAYPAL_MODE=sandbox
# PAYPAL_CLIENT_ID=
# PAYPAL_CLIENT_SECRET=
# PAYPAL_WEBHOOK_ID=
CONFIDENTIAL_TOKEN_ONRAMP=false # confidential+ BuyNativeViaCard (native_token_onramp)
NEXT_PUBLIC_CONFIDENTIAL_TOKEN_ONRAMP=false
# Token Desk (credit→RING) is subscriber+ — no confidential env gate| Fixed Fee |
|---|
| Currency |
|---|
| Status |
|---|
| WayForPay | 2.5% | — | UAH | Live |
| Stripe | 2.9% | $0.30 | USD | Live |
| Credit Balance | 0% | — | USD | Live |
| RING Token | 0% | — | RING | Live |
| PayPal | 2.9% | $0.30 | USD | Live (Orders + membership Subscriptions) |
There is no WAYFORPAY_MERCHANT_ID. Use WAYFORPAY_MERCHANT_ACCOUNT. WAYFORPAY_MERCHANT_PASSWORD is required for recurring / regularApi subscription management.
Blank PAYMENT_*_PROCESSOR falls back to PAYMENT_DEFAULT_PROCESSOR (default wayforpay). Set it to stripe for USD-first clones.
lib/payments/processors/wayforpay.processor.ts |
Per-purpose checkout; HPP via wayforpay-hpp.ts |
| Stripe | lib/payments/processors/stripe.processor.ts | Stripe Checkout sessions (navigate) |
| Internal credit | lib/payments/processors/internal-credit.processor.ts | Credit balance deduction |
| Native token | lib/payments/processors/native-token.processor.ts | Synchronous on-chain RING → treasury |
| PayPal | lib/payments/processors/paypal.processor.ts | Orders v2 approve URL (navigate); store + wallet_topup + membership one-shot |
| Webhook dispatcher | lib/payments/conductor/webhook-dispatcher.ts | Orders capture by purpose + PayPal Subscriptions lifecycle branch |
| Purpose handlers | lib/payments/conductor/handlers/*.ts | Fulfillment after paid (incl. membership-paypal-subscription.ts) |
PaymentProcessorId (conductor/types.ts): wayforpay | stripe | internal-credit | native-token | paypal.
| Rail | Description | Env gate |
|---|---|---|
merchant_redirect | WayForPay / Stripe hosted checkout | Default |
internal_credit | Wallet credit balance (fiat points) | PAYMENT_STORE_ALLOW_CREDIT |
native_token | RING token on-chain to treasury | PAYMENT_STORE_ALLOW_TOKEN |
| Endpoint | Provider |
|---|---|
/api/payments/wayforpay/webhook | WayForPay (HMAC) |
/api/payments/stripe/webhook | Stripe (STRIPE_WEBHOOK_SECRET) |
/api/payments/paypal/webhook | PayPal (transmission signature + PAYPAL_WEBHOOK_ID) |
internal_credit and native_token settle synchronously inside createCheckout (no external webhook) — the processor debits credit / transfers on-chain and marks the ledger row paid before returning.
| Route | Rail | Notes |
|---|---|---|
POST /api/store/payments/wayforpay | merchant_redirect | createCheckout({ purpose: 'store_order' }); initiateStorePayment is an internal helper inside wayforpay.processor.ts, not a ledger bypass |
POST /api/store/payments/stripe | merchant_redirect | Stripe Checkout session |
POST /api/store/payments/token | native_token | Requires PAYMENT_STORE_ALLOW_TOKEN=true; isRailEnabled('store_order', 'native_token') gate |
POST /api/store/payments/credit | internal_credit | Credit balance deduction |
POST /api/store/payments/paypal | merchant_redirect | Orders v2; requires PayPal credentials + gateways.paypal.enabled + public flag |
POST /api/store/payments/card | merchant_redirect | Alias of WayForPay card path |
createNativeTokenCheckout (native-token.processor.ts) resolves fiat → token via the oracle (nativeOut = amount / ringPerUsd, or explicit metadata.tokenAmount), checks balance, then transferNativeTokenForUser → treasury and markPaid. Fails closed with NATIVE_TOKEN_RAIL_DISABLED, TREASURY_NOT_CONFIGURED, or INSUFFICIENT_TOKEN_BALANCE.
initiateCreditTopupPayment in app/_actions/wallet.ts → purpose: 'wallet_topup' → createWalletTopupWayForPay (or Stripe) → webhook handlers/wallet-topup.ts / wallet-topup-stripe.ts credits fiat USD points via creditBalanceService.addFiatUsd (1:1). This is distinct from the native_token rail: card top-up never mints on-chain RING.
POST /api/membership/payment/paypal → SubscriptionConductor provider paypal:
auto_subscribe) — PayPal Subscriptions v1; ledger pending → BILLING.SUBSCRIPTION.ACTIVATED → active; cancel uses paypal_subscription_id.recordPaidSubscription (never re-enters provider create).PaymentModal PayPal tab: components/membership/payment-modal.tsx when NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=true. Member manage page: /membership/manage — see SubscriptionConductor.
Wallet Add Credit PayPal still uses Orders on wallet_topup when credentials are configured.
Apply data/migrations/004_payment_transactions.sql before production. Ledger rows use the DatabaseService result contract { success, data, error }.
Credit top-up via WayForPay / Stripe / PayPal (wallet_topup).
{
"payment": {
"cardPaymentProcessor": "wayforpay",
"supportedMethods": ["wayforpay", "credit_balance", "native_token"],
"gateways": {
"wayforpay": { "enabled": true, "feePercent": 2.5, "currency": "UAH" },
"stripe": { "enabled": false, "feePercent": 2.9, "feeFixedCents": 30, "currency": "USD" }
}
}
}
PAYMENT_STORE_PROCESSOR=stripe
PAYMENT_MEMBERSHIP_PROCESSOR=stripe
PAYMENT_NEWS_PROCESSOR=wayforpay
PAYMENT_WALLET_TOPUP_PROCESSOR=wayforpay
PAYMENT_STORE_ALLOW_CREDIT=true # spend credit points at store checkout
PAYMENT_STORE_ALLOW_TOKEN=false # spend native token at store checkout
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_TOKEN=false
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=false
# PAYPAL_MODE=sandbox
# PAYPAL_CLIENT_ID=
# PAYPAL_CLIENT_SECRET=
# PAYPAL_WEBHOOK_ID=
CONFIDENTIAL_TOKEN_ONRAMP=false # confidential+ BuyNativeViaCard (native_token_onramp)
NEXT_PUBLIC_CONFIDENTIAL_TOKEN_ONRAMP=false
# Token Desk (credit→RING) is subscriber+ — no confidential env gate