users 狀態變動
文件狀態:草稿 / 已補 provider payer 排查視角
最後驗證:2026-05-07
來源:quote bid contact charge trace、provider_accept_narrow_match migration / staging 驗證、Common Process Documents
目的
users 是身份主表,但不應寫成所有 user 相關資訊的大雜燴。這裡只整理跨流程會影響 request / bid / payment / notification 的身份狀態。
完整 search_add trace 請先看 業務流程/search_add;本資料夾只保留 actor / requestor / provider / payer 的身份視角。
這份文件目前優先回答 quote bid contact charge 與 narrow match accept 裡最容易混淆的問題:
誰是登入者?
誰是 consumer / requestor?
誰是 provider?
誰被扣款?
扣款失敗時要回頭看 users 的哪些狀態?相關業務流程
- users_delete:使用者刪除帳號時,
users停用、email / phone 加DEL{timestamp}_prefix、服務/案件歸檔、session 清除與 downstream queue。
不回答什麼
- 不整理
users全欄位定義。 - 不記錄單次 staging 測試 user id、session token 或付款 token。
- 不把 provider 資格、封鎖、錢包、卡片全部塞進 users;這些通常還要看關聯表與 application log。
- 不用
users單表判斷付款成功;付款成功要回到transactions與 quote bid contact 狀態。
使用者在不同流程可能扮演:
| 角色 | 常見關聯 |
|---|---|
| requestor / consumer | quote_requests.user_id, message sender, notification receiver |
| provider / pro | quote_bids.provider_user_id, quote_services.user_id, transaction payer |
| session actor | API session token 對應 user |
| payer | quote bid contact charge 時通常是 provider,也就是 transactions.user_id |
| notification receiver | 依事件不同可能是 consumer 或 provider |
它連到哪些資料
user_profiles:姓名、電話等 profile 資料。api_sessions:API session token 與 device。quote_requests:requestor。quote_services:provider service owner。quote_bids:provider / requestor bid context。transactions:付款主體通常是 provider user。stripe_customers:provider card / auto quote card。blocked_user_configs/blocked_chat_users:接案資格、封鎖與互動限制。
Provider payer 視角
quote bid contact charge 的付款主體是 provider,不是 consumer。
例如 provider_accept_narrow_match 成功 accept 後,流程會轉進 quote bid contact charge。這時 consumer 是邀請者 / requestor,但實際付費接觸新客源的是 provider:
API session token
-> session actor user id
-> 必須等於 quote_bids.provider_user_id
-> provider 正式 contact quote request
-> users.available_wallet_amount / provider card / TapPay prime
-> transactions.user_id = provider user id
-> transactions.class = QuoteBid
-> transactions.foreign_id = quote_bid_id對照 requestor:
quote_requests.user_id = consumer / requestor
quote_bids.provider_user_id = provider / payer
transactions.user_id = provider / payer所以排查付款時,不要從 quote_requests.user_id 去查 wallet 或 card。應該先從 quote_bids.provider_user_id 找 provider,再查 provider 的 wallet / card / qualification。
常見流程中的身份語意
| 流程 | session actor | requestor / consumer | provider | payer |
|---|---|---|---|---|
search_add | 建立需求者或匿名/session user | quote_requests.user_id | 可能由 match 產生 bid | 通常無同步扣款 |
consumer_want_provider_quote | consumer / request owner | quote_requests.user_id | quote_bids.provider_user_id | 無,這是邀請 provider 報價 |
provider_accept_narrow_match | provider | quote_requests.user_id | quote_bids.provider_user_id | provider |
want_to_contact_provider | 依入口而定 | quote_requests.user_id | quote_bids.provider_user_id | provider contact charge 主線中是 provider |
consumer_want_provider_quote 和 provider_accept_narrow_match 容易被混在一起:
consumer_want_provider_quote
-> consumer 邀請 provider 報價
-> 不扣 provider 款
provider_accept_narrow_match
-> provider 接受 invitation
-> 成功後進 contact charge
-> provider 是 payerProvider payer 要看的 users 狀態
排查 provider 付款 / contact 失敗時,users 主要提供幾種線索:
| 線索 | 常見欄位 / 關聯 | 用途 |
|---|---|---|
| actor 是否正確 | api_sessions.user_id, users.id | session user 是否為 bid provider |
| provider 基本資格 | users.is_phone_confirmed, user_profiles.phone | phone / profile guard 是否可能擋 contact |
| provider wallet | users.available_wallet_amount | wallet 是否足以支付 quote bid fee |
| provider card | stripe_customers | wallet 不足時是否有可用 card / auto quote payment method |
| provider 封鎖 / 限制 | blocked_user_configs, blocked_chat_users, qualification tables | 是否因資格或封鎖無法聯繫 consumer |
這些狀態通常不能單獨解釋 API response。付款與 contact 失敗要先看 application log:
channel = processWantToContactProvider
channel = logQuoteBidTxn
channel = getCheckConnect再回查 users、quote_bids、transactions。
常見錯誤與 users 關聯
| Response / internal code | 常見意義 | users/provider payer 視角 |
|---|---|---|
3 | 呼叫者不是 provider | session actor 不等於 quote_bids.provider_user_id |
10 | 無可用付款方式 / 要求設定信用卡 | provider wallet 不足,且沒有可用 card / prime |
211 | 外國卡或不支援卡片 | provider card / TapPay branch 失敗 |
251 | provider 尚未完成接案資格 | provider qualification / certification 不符合 |
256 | provider 封鎖 consumer | provider 與 requestor 的 block/chat restriction |
690 | provider 目前無法聯繫 | provider 資格、phone、狀態或其他 guard |
API response 的 error 是對外包裝。相同 error=10 背後可能是 wallet 不足、沒有 card、沒有 prime、金流 provider 拒絕或 transaction 前置 guard。應以 processWantToContactProvider log 的 context 為準。
建議閱讀順序
- quote_bid_contact_charge
- Log channel 與 error code 索引
- transactions
- provider_accept_narrow_match
- narrow_match
角色.md欄位/is_phone_confirmed.md欄位/available_wallet_amount.md關聯/api_sessions.md關聯/user_profiles.md關聯/stripe_customers.md關聯/blocked_user_configs.md關聯/blocked_chat_users.md
核心觀念
同一個 users.id 在不同流程的語意不同。排查前先確認它是:
session actor
requestor
provider
transaction payer
notification receiver例如 provider_accept_narrow_match 中:
session actor = provider
quote_bids.provider_user_id = provider
quote_requests.user_id = requestor
transactions.user_id = provider payer排查順序
provider payer 相關問題建議照這個順序,不要先從 users 單表猜原因。
- 先看 application log。
channel = processWantToContactProvider
channel = logQuoteBidTxn
channel = getCheckConnect- 確認 bid 的 requestor / provider。
SELECT
qb.id,
qb.provider_user_id,
qb.quote_request_id,
qb.quote_service_id,
qb.is_want_to_contact_provider,
qb.contact_provider_on,
qb.total_site_fee,
qb.quote_user_subscription_log_id,
qr.user_id AS request_user_id,
qr.is_closed,
qr.is_expired,
qr.hired_count
FROM quote_bids qb
INNER JOIN quote_requests qr ON qr.id = qb.quote_request_id
WHERE qb.id = <quote_bid_id>;- 用
provider_user_id查 provider user。
查資料前先依 repo 規則 SHOW COLUMNS,不要猜欄位。常見會看的欄位:
SELECT
id,
is_phone_confirmed,
available_wallet_amount
FROM users
WHERE id = <provider_user_id>;- 查 provider profile / card / block / qualification。
這些表的欄位差異比較容易踩錯,先查 schema 再查資料:
user_profiles
stripe_customers
blocked_user_configs
blocked_chat_users
provider qualification / certification related tables- 查交易是否成立。
SELECT
id,
created,
user_id,
class,
foreign_id,
amount,
real_pay_amount,
real_pay_date,
stripe_charge_id
FROM transactions
WHERE class = 'QuoteBid'
AND foreign_id = <quote_bid_id>
ORDER BY id DESC;判讀重點:
transactions.user_id 應該是 provider_user_id
transactions.foreign_id 應該是 quote_bid_id
real_pay_date 有值通常才代表已付款
stripe_charge_id 可能是 Stripe charge id,也可能是 TapPay rec_trade_id手動驗證提醒
如果用 staging curl 驗 provider_accept_narrow_match:
沒有 prime
-> 代表這次 request 沒帶 TapPay 一次性付款 token
-> 若 provider wallet 不足且沒有可用 card,可能回 error=10 要求設定信用卡
有 prime
-> 代表本次 request 帶 TapPay token
-> 成功時可進 TapPay pay-by-prime,交易識別可能寫入 transactions.stripe_charge_id第一次 no-prime 失敗和第二次帶 prime 成功,可以發生在同一筆 bid 的前後兩次 request。排查時要用時間序確認前一次失敗沒有留下 accepted/contact/transaction 成功狀態,後一次成功才更新 bid 與 transaction。
適合優先補的文件
角色.md:requestor / provider / payer / receiver 的判讀。欄位/is_phone_confirmed.md:報價與接案流程的電話驗證 guard。欄位/available_wallet_amount.md:wallet 扣款前後如何判斷。關聯/api_sessions.md:session token 如何決定 actor。關聯/stripe_customers.md:provider card 與 TapPay / Stripe 扣款能力。