---
title: "Payment Gateway Integration"
description: "Operator guide — PaymentConductor, WayForPay, Stripe, PayPal, and internal credit for store, membership, news, wallet top-up, and public-pool card jars"
locale: "en"
---
# Payment Gateway Integration

  Ring routes card and credit flows through **PaymentConductor** — one ledger (`payment_transactions`), one webhook dispatcher, config-driven processors. Operators configure env vars and PSP dashboards; developers read [PaymentConductor architecture](/docs/architecture/payment-conductor.md). DAO jar card chip-ins use purpose **`public_pool_contribution`** ([Public Pools](/docs/features/public-pools.md)) — not WayForPay/Stripe/PayPal hosted “donation” products.

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

  
- **[PaymentConductor feature](/docs/features/payment-conductor.md)** — Purposes, rails, env overview

  
- **[Public Pools & DAO Jars](/docs/features/public-pools.md)** — Desk-oracle card jar, fees, escrow gate

  
- **[Architecture deep dive](/docs/architecture/payment-conductor.md)** — Handlers, ledger, idempotency, API routes

  
- **[WayForPay integration](/docs/features/wayforpay-integration.md)** — Env SSOT, HMAC, regularApi password

```mermaid
sequenceDiagram
  participant U as User
  participant R as Ring API
  participant PC as PaymentConductor
  participant PSP as WayForPay / Stripe
  participant DB as payment_transactions

  U->>R: Checkout / upgrade / promotion / top-up / jar chip-in
  R->>PC: createCheckout(purpose)
  PC->>DB: createPending(orderReference)
  PC->>PSP: redirect URL
  PSP->>U: Hosted payment
  PSP->>R: POST webhook
  R->>PC: dispatch webhook
  PC->>DB: mark paid
  PC->>R: purpose handler fulfills
```

## Payment purposes

| Purpose | Typical use | Handler |
|---------|-------------|---------|
| `store_order` | Multi-vendor store checkout | `conductor/handlers/store-order.ts` |
| `membership_upgrade` | SUBSCRIBER → MEMBER | `conductor/handlers/membership-upgrade.ts` |
| `news_promotion` | News Kingdom paid promotion | `conductor/handlers/news-promotion.ts` |
| `wallet_topup` | Wallet credit top-up (card) | `conductor/handlers/wallet-topup.ts` |
| `public_pool_contribution` | Public pool / DAO jar card or PayPal | `conductor/handlers/public-pool-contribution.ts` |

**Processors:** WayForPay (default), Stripe, internal credit, native token, **PayPal** (Orders for store/wallet/jars; Subscriptions v1 for recurring membership — see [SubscriptionConductor](/docs/features/subscriptions.md)).

**Rails:** `merchant_redirect` · `credit_balance` (`PAYMENT_STORE_ALLOW_CREDIT`) · `native_token` (`PAYMENT_STORE_ALLOW_TOKEN`).

Apply `data/migrations/004_payment_transactions.sql` before production payments.

### For founders

## Operator setup

### Environment variables

{`PAYMENT_DEFAULT_PROCESSOR=wayforpay
PAYMENT_STORE_PROCESSOR=wayforpay
PAYMENT_MEMBERSHIP_PROCESSOR=wayforpay
PAYMENT_NEWS_PROCESSOR=wayforpay
PAYMENT_WALLET_TOPUP_PROCESSOR=wayforpay
# Optional: PAYMENT_PUBLIC_POOL_CONTRIBUTION_PROCESSOR=wayforpay
WAYFORPAY_MERCHANT_ACCOUNT=your_merchant
WAYFORPAY_SECRET_KEY=your_secret
WAYFORPAY_MERCHANT_PASSWORD=your_password
WAYFORPAY_DOMAIN=ring-platform.org
WAYFORPAY_API_URL=https://api.wayforpay.com/api
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
PAYMENT_FIAT_CURRENCY=USD
PAYMENT_STORE_ALLOW_CREDIT=true
PAYMENT_STORE_ALLOW_TOKEN=false
NEXT_PUBLIC_PAYMENT_STORE_ALLOW_PAYPAL=false
# PAYPAL_MODE=sandbox
# PAYPAL_CLIENT_ID=
# PAYPAL_CLIENT_SECRET=
# PAYPAL_WEBHOOK_ID=`}

> **Warning**
> **No `WAYFORPAY_MERCHANT_ID`.** Use `WAYFORPAY_MERCHANT_ACCOUNT`. Set `WAYFORPAY_DOMAIN` to your clone's approved domain (for this product: `ring-platform.org`). `WAYFORPAY_MERCHANT_PASSWORD` is required for recurring / `regularApi`.

### Webhook URLs (register in PSP cabinet)

Replace the host with your clone domain:

| Provider | Service URL |
|----------|-------------|
| WayForPay | `https://ring-platform.org/api/payments/wayforpay/webhook` |
| Stripe | `https://ring-platform.org/api/payments/stripe/webhook` |

> **Warning**
> **Public HTTPS required.** WayForPay / Stripe cannot POST to `localhost`. Local card tests will open the PSP page, but **credit will not settle** until `serviceUrl` is reachable (tunnel or staging). Ring emits `returnUrl` per request — you do **not** need a static thank-you page in the WayForPay cabinet. See [WayForPay](/docs/features/wayforpay-integration.md).

  Legacy paths `/api/payments/wayforpay/callback`, `/success`, `/failure` — **not implemented**.

Deprecated aliases (still work — migrate when convenient):

- `/api/store/payments/wayforpay/webhook`
- `/api/news/promotion/wayforpay-webhook`

### Pre-launch checklist

Migration `004_payment_transactions.sql` applied on production Postgres.

`PAYMENT_*` and WayForPay / Stripe credentials in secrets manager (never commit `.env.local`).

Canonical webhook URLs registered; `WAYFORPAY_DOMAIN` matches the approved merchant domain.

Staging smoke: store checkout, membership card upgrade, news promotion (if used), wallet credit top-up.

Post go-live: monitor `payment_transactions` for stuck `pending` / `redirected` rows; use Admin → user → Payments tab for membership / wallet top-up history.

### For developers

## Routes and modules

| Route file | Method | Role |
|------------|--------|------|
| `app/api/payments/wayforpay/webhook/route.ts` | POST | `dispatchWayForPayWebhook` |
| `app/api/payments/stripe/webhook/route.ts` | POST | `dispatchStripeWebhook` |
| `app/api/store/payments/wayforpay/route.ts` | POST | Store redirect via `PaymentConductor` (`store_order`) |
| `app/api/store/payments/credit/route.ts` | POST | Internal credit checkout |
| `app/api/store/payments/[orderId]/status/route.ts` | GET | Poll order status |
| `app/api/admin/users/[id]/payments/route.ts` | GET | Admin Payments tab (`listByUserId`) |

WayForPay routes by `orderReference` prefix (`store_`, `membership_`, `news-promo-…`, `wallettopup_`) — see `lib/payments/conductor/webhook-dispatcher.ts` and `lib/payments/order-reference.ts`.

### PSP setup — WayForPay

Create a merchant account at [wayforpay.com](https://wayforpay.com) — KYC, note **merchant account**, **secret key**, **merchant password**, approved **domain**.

Set Ring env vars from `env.local.template` (also mirrored in `docker.env.template` / `docker-compose.template.yml`).

Cabinet **service URL**: `https:///api/payments/wayforpay/webhook`.

Staging: migration `004` → test checkout / membership / wallet top-up → confirm `payment_transactions` + fulfillment.

### PSP setup — Stripe

Create account at [stripe.com](https://stripe.com); obtain test or live API keys.

{`PAYMENT_DEFAULT_PROCESSOR=stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...`}

Dashboard → Webhooks → endpoint `https:///api/payments/stripe/webhook`. Subscribe to `checkout.session.completed` (and events your processor handles).

## Related

  
- **[Payments feature](/docs/features/payments.md)** — Membership flow and overview

  
- **[Store API](/docs/api/store.md)** — Checkout via PaymentConductor `store_order`

  
- **[Wallet](/docs/features/wallet.md)** — Live WayForPay credit top-up

  
- **[Migrations](/docs/getting-started/migrations.md)** — Apply order including migration 004
