| 73 |
73 |
|
let item_id_display = item_id.map(|id| id.to_string()).unwrap_or_else(|| "project".to_string());
|
| 74 |
74 |
|
|
| 75 |
75 |
|
// Get the payment intent ID
|
| 76 |
|
- |
let payment_intent_id = session.payment_intent.clone().unwrap_or_else(|| "unknown".to_string());
|
|
76 |
+ |
// Display/logging copy only; the DB write below passes `session.payment_intent`
|
|
77 |
+ |
// directly so a PI-less session stores NULL, not a literal "unknown" that would
|
|
78 |
+ |
// collide with other PI-less rows in the money-keyed lookup column (Run 9).
|
|
79 |
+ |
let payment_intent_id = session.payment_intent.clone().unwrap_or_default();
|
| 77 |
80 |
|
|
| 78 |
81 |
|
// Complete the transaction (idempotent - returns None if already completed).
|
| 79 |
82 |
|
// Steps 1-3 (complete_transaction, increment_sales_count, discount code increment)
|
| 80 |
83 |
|
// are wrapped in a single DB transaction to prevent inconsistent state if any step fails.
|
| 81 |
84 |
|
let mut db_tx = state.db.begin().await.context("begin purchase webhook transaction")?;
|
| 82 |
85 |
|
|
| 83 |
|
- |
match db::transactions::complete_transaction(&mut *db_tx, &session_id, &payment_intent_id).await {
|
|
86 |
+ |
match db::transactions::complete_transaction(&mut *db_tx, &session_id, session.payment_intent.as_deref()).await {
|
| 84 |
87 |
|
Ok(Some(tx)) => {
|
| 85 |
88 |
|
tracing::info!(
|
| 86 |
89 |
|
buyer_id = %buyer_id, seller_id = %seller_id, item_id = %item_id_display, amount_cents = %tx.amount_cents,
|
| 191 |
194 |
|
let buyer_id = meta.buyer_id;
|
| 192 |
195 |
|
let seller_id = meta.seller_id;
|
| 193 |
196 |
|
|
| 194 |
|
- |
let payment_intent_id = session.payment_intent.clone().unwrap_or_else(|| "unknown".to_string());
|
|
197 |
+ |
// Display/logging copy only; the DB write below passes `session.payment_intent`
|
|
198 |
+ |
// directly so a PI-less session stores NULL, not a literal "unknown" that would
|
|
199 |
+ |
// collide with other PI-less rows in the money-keyed lookup column (Run 9).
|
|
200 |
+ |
let payment_intent_id = session.payment_intent.clone().unwrap_or_default();
|
| 195 |
201 |
|
|
| 196 |
202 |
|
// Complete ALL pending transactions for this session in a single DB transaction
|
| 197 |
203 |
|
let mut db_tx = state.db.begin().await.context("begin cart webhook transaction")?;
|
| 198 |
204 |
|
|
| 199 |
205 |
|
let completed_txs = db::transactions::complete_cart_transactions(
|
| 200 |
|
- |
&mut *db_tx, &session_id, &payment_intent_id,
|
|
206 |
+ |
&mut *db_tx, &session_id, session.payment_intent.as_deref(),
|
| 201 |
207 |
|
)
|
| 202 |
208 |
|
.await
|
| 203 |
209 |
|
.context("complete cart transactions")?;
|
| 588 |
594 |
|
let tipper_id = metadata.tipper_id;
|
| 589 |
595 |
|
let recipient_id = metadata.recipient_id;
|
| 590 |
596 |
|
|
| 591 |
|
- |
let payment_intent_id = session.payment_intent.clone().unwrap_or_else(|| "unknown".to_string());
|
| 592 |
|
- |
|
| 593 |
|
- |
// Complete the tip (idempotent)
|
| 594 |
|
- |
match db::tips::complete_tip(&state.db, &session_id, &payment_intent_id)
|
|
597 |
+ |
// Complete the tip (idempotent). A PI-less session stores NULL (not a literal
|
|
598 |
+ |
// "unknown") in the money-keyed lookup column (Run 9).
|
|
599 |
+ |
match db::tips::complete_tip(&state.db, &session_id, session.payment_intent.as_deref())
|
| 595 |
600 |
|
.await
|
| 596 |
601 |
|
.context("complete tip")? {
|
| 597 |
602 |
|
Some(tip) => {
|
| 649 |
654 |
|
.unwrap_or("unknown@guest")
|
| 650 |
655 |
|
.to_string();
|
| 651 |
656 |
|
|
| 652 |
|
- |
let payment_intent_id = session.payment_intent.clone().unwrap_or_else(|| "unknown".to_string());
|
|
657 |
+ |
// Display/logging copy only; the DB write below passes `session.payment_intent`
|
|
658 |
+ |
// directly so a PI-less session stores NULL, not a literal "unknown" that would
|
|
659 |
+ |
// collide with other PI-less rows in the money-keyed lookup column (Run 9).
|
|
660 |
+ |
let payment_intent_id = session.payment_intent.clone().unwrap_or_default();
|
| 653 |
661 |
|
|
| 654 |
662 |
|
// Complete the guest transaction and increment sales count in a single DB transaction
|
| 655 |
663 |
|
// (matching the non-guest path pattern to prevent counter drift on partial failure)
|
| 664 |
672 |
|
match db::transactions::complete_guest_transaction(
|
| 665 |
673 |
|
&mut *db_tx,
|
| 666 |
674 |
|
&session_id,
|
| 667 |
|
- |
&payment_intent_id,
|
|
675 |
+ |
session.payment_intent.as_deref(),
|
| 668 |
676 |
|
&guest_email,
|
| 669 |
677 |
|
).await? {
|
| 670 |
678 |
|
Some(tx) => {
|