feat(economy): purchase_items primitive + entitlement import seeding
purchase_items: generic atomic debit + mint of several items (fail-closed) for open-on-buy Store packs (Store BUY returns items immediately) + POST /economy/purchase-items route. Import: add optional entitlements[] to ProfileImportRequest, seeding unconsumed packs rows in the same transaction (so a source's unopened packs become Core entitlements); idempotency via the existing import fingerprint. Tests: 2 purchase_items unit + endpoint + entitlement-seed import. Core matrix 45 lib + 116 integration + 9 import green; clippy clean.
This commit is contained in:
@@ -51,6 +51,13 @@ pub struct ImportOwnedCard {
|
||||
pub card_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ImportEntitlement {
|
||||
/// Opaque definition reference for one unconsumed entitlement (e.g. a pack
|
||||
/// id as text). Core stores it verbatim; it never interprets the value.
|
||||
pub definition_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ImportSlot {
|
||||
pub owned_item_id: String,
|
||||
@@ -93,6 +100,9 @@ pub struct ProfileImportRequest {
|
||||
pub owned: Vec<ImportOwnedCard>,
|
||||
#[serde(default)]
|
||||
pub squad: Option<ImportSquad>,
|
||||
/// Unconsumed entitlements to seed (e.g. from a source's unopened packs).
|
||||
#[serde(default)]
|
||||
pub entitlements: Vec<ImportEntitlement>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||
@@ -244,6 +254,19 @@ pub async fn apply_profile_import(
|
||||
.with_context(|| format!("insert owned_card {}", o.owned_item_id))?;
|
||||
}
|
||||
|
||||
for e in &req.entitlements {
|
||||
sqlx::query(
|
||||
"INSERT INTO packs (id, club_id, definition_id, opened, created_at) VALUES (?, ?, ?, 0, ?)",
|
||||
)
|
||||
.bind(Uuid::new_v4().to_string())
|
||||
.bind(&club_id)
|
||||
.bind(&e.definition_id)
|
||||
.bind(&now)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.with_context(|| format!("insert entitlement {}", e.definition_id))?;
|
||||
}
|
||||
|
||||
let mut squad_slots = 0usize;
|
||||
if let Some(sq) = &req.squad {
|
||||
let squad_id = Uuid::new_v4().to_string();
|
||||
|
||||
Reference in New Issue
Block a user