- payment domain: payments/memberships/attempts/receipts/refunds/commands/events, legacy-demo provenance (0024), checkout header-hash idempotency (0025) - wallet collection all-or-none across shop orders; group-wide cancellation; checkout cutover, status-only payOrder removed - Stripe crypto-only Checkout adapter, raw-body HMAC webhooks, durable command worker with leases - original-method refunds with receipt/allocation budgets; refund_pending/refund_failed aftersale states - admin channel CRUD/revisions/audit/verify + operations list/detail/reconcile; secret-ref allowlist (VMALL_PAYMENT_*), env-fixed demo/sandbox/live - mall/mobile checkout pay surfaces, admin console pages; shared contract updated - pre-launch leftovers recorded in docs/TBD-payments.md cargo test -p vmall-api green twice; four frontend builds green; browser smoke: mall wallet checkout paid, admin channels/operations verified
25 lines
943 B
Rust
25 lines
943 B
Rust
use sqlx::PgPool;
|
|
|
|
#[derive(Clone)]
|
|
pub struct AppState {
|
|
pub db: PgPool,
|
|
pub redis: redis::aio::ConnectionManager,
|
|
pub config: crate::config::Config,
|
|
}
|
|
|
|
pub async fn build_state(config: &crate::config::Config) -> anyhow::Result<AppState> {
|
|
let db = PgPool::connect(&config.database_url).await?;
|
|
assemble(config.clone(), db).await
|
|
}
|
|
|
|
/// Build state from an already-open pool (migrate + serve share one pool).
|
|
pub async fn assemble(config: crate::config::Config, db: PgPool) -> anyhow::Result<AppState> {
|
|
let client = redis::Client::open(config.redis_url.clone())?;
|
|
let redis = redis::aio::ConnectionManager::new(client).await?;
|
|
let state = AppState { db, redis, config };
|
|
// Demo deployments retain exactly one explicitly labeled demo wallet
|
|
// channel; no enabled external channel is created by default.
|
|
crate::modules::payment::service::ensure_demo_wallet_channel(&state).await?;
|
|
Ok(state)
|
|
}
|