feat(payment): route FlatRender plan purchases through the broker
CI/CD / CI · Web (tsc) (push) Successful in 1m10s
CI/CD / Deploy · full stack (push) Failing after 11m4s

- identity: when FlatPay (broker) is configured, InitiateZarinPalAsync
  routes through pay.flatrender.ir instead of calling ZarinPal directly;
  new HandleBrokerCallbackAsync confirms the payment via the broker
  inquiry API (authoritative, not trusting the redirect) and activates
  the plan. New public endpoint GET /v1/payments/callback/broker
  (already public at the gateway via /callback/*). Env-gated — empty
  FlatPay__ApiKey keeps the legacy direct-ZarinPal path.
- broker: deliver webhooks inline on enqueue (best-effort) in addition
  to the retry loop, so clients credit near-instantly (db.GetWebhook +
  goroutine kick).
- compose + ENV_FILE: FlatPay__* for identity (FLATPAY_FLATRENDER_*).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-16 00:34:45 +03:30
parent ec51e87d2d
commit 376cdf6a1c
7 changed files with 187 additions and 2 deletions
+7
View File
@@ -68,6 +68,13 @@ PAY_PUBLIC_URL=https://pay.flatrender.ir
# payment which unit YOUR merchant settles in, then set this to match.
ZARINPAL_AMOUNT_UNIT=rial
# FlatRender's OWN plan purchases through the broker. Create a "flatrender" client
# app in Admin → پرداخت (allowed origin https://api.flatrender.ir), then paste its
# key+secret here. Empty ⇒ identity calls ZarinPal directly (legacy).
FLATPAY_FLATRENDER_API_KEY=
FLATPAY_FLATRENDER_SECRET=
FLATPAY_RETURN_BASE=https://api.flatrender.ir
# ── Payments (fill the providers you use; leave others blank) ────────────────
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=