老闆 dogfood 問:「點同意退款會真的退款嗎?藍新測試環境會收到 callback 嗎?」
Audit 結論:不會 —目前 POS 同意退款只是「假退款」,完全沒呼叫藍新。
老闆選 Option C → 已 ship orchestrator dispatch(等老闆 sandbox 測試卡 dogfood)
deposit_payment_method + deposit_payment_id 傳 orchestratornewebpay → 呼叫 newebpay-refund edge function(藍新 sync API)manual_transfer → 走既有 setCancellationResolution mark refundedbumpRefundAttempt,既有 retry-failed-refunds cron 自動 retry 5 次後升 manualrefund_dispatch + refund_error 給 POS 顯示deposit_status='refunded' + refund_status='succeeded' 假裝退款,但完全沒呼叫 newebpay-refund edge function(該 function 已實作但從未被 cancellation flow 呼叫過)。
POS 老闆按「同意取消並退款」
↓
[POST /api/reservation/<id>/cancellation/approve]
body: { refund_kind: 'refund', refund_amount, refund_account_digits, ... }
↓
[approveCancellation orchestrator]
↓
[reservationRepo.setCancellationResolution]
CASE refund:
deposit_status = 'refunded' ← 假設成功
refund_status = 'succeeded' ← 假設成功
refunded_at = now ← 假設成功
refund_amount, refund_account_last_four, refund_note 寫入
↓
[fire-and-forget triggerCancellationEmail]
→ send-cancellation-email edge function (寄信通知客人)
↓
return success
❌ 完全沒呼叫 newebpay-refund edge function
deposit_status=refunded, refund_status=succeeded)→ 老闆以為退了實際 server 設 deposit_status='refunded' → POS badge filter 確實看 .paid 才算,理論上 .refunded 不算。但 SwiftData sync lag 老闆當下看到沒更新。
短期已修(commit 3dd61a5f):POS filter 加 refundedAt == nil 排除已退款。Long-term 需 sync handler 改進。
客人原本用藍新刷卡付訂金 → 老闆同意退款 → server 自動呼叫藍新 CreditReturn API。
POS 老闆按「同意取消並退款」
↓
[approveCancellation orchestrator]
↓
[setCancellationResolution]
refund_status = 'processing' ← 改成 pending(等藍新 callback)
deposit_status 暫不改 ← 等 callback 才設 refunded
↓
[invoke supabase functions newebpay-refund]
body: { action: 'deposit-refund', payment_id, amount, reservation_id }
↓
[藍新呼叫 CreditReturn API]
├─ 成功 → 藍新 callback → /api/newebpay/deposit/notify
│ → set deposit_status='refunded' + refund_status='succeeded'
│ → 寄 confirmation email
└─ 失敗 → callback 帶 error → set refund_status='failed' + refund_failed_reason
→ cron retry-failed-refunds 重試 / 升級 manual_required
POS 在這段時間 badge 顯示「⏳ 處理中」直到 callback。
優點:全自動,客人立刻收到藍新退款。
缺點:只能用於藍新付的訂位(手動匯款的不適用)。
客人原本用銀行轉帳付訂金 → 老闆同意退款 → 老闆自己 ATM 匯回客人 → POS 標記完成。
POS 老闆按「同意取消並退款」 ↓ [setCancellationResolution] deposit_status = 'refunded'(立刻設) refund_status = 'manual_completed' refund_account_last_four 記錄(老闆已 ATM 匯款的帳號) ↓ [寄 email 給客人] 「店家已退款 NT$X 至 ATM 帳號 ****X,1-3 天到帳」
優點:手動匯款的訂位可用。
缺點:老闆要手動操作 ATM,不靠藍新。
看 reservation.deposit_payment_method 自動判斷,涵蓋所有訂位類型。
'newebpay' → 走 Option A 自動藍新退款'bank_transfer'(手動匯款 / 客人匯銀行)→ 走 Option B 手動模式POS UI 也對應改成:
優點:涵蓋所有情境,UX 對應 method。
缺點:工作量稍多(需 dispatch + UI 分流)。
code=SUCCESS test mode)https://chefsmate.app/api/newebpay/deposit/notify(production)+ ngrok / preview env?
retry-failed-refunds cron 5 次後升級 manual_required,要保留嗎?newebpay-refund edge function 已存在但未實際被 cancellation flow 呼叫過(0 production traffic),要先 sandbox dogfood| 檔案 | 狀態 |
|---|---|
supabase/functions/newebpay-refund/index.ts | edge function 已實作 |
supabase/functions/retry-failed-refunds/index.ts | cron retry 已實作 |
supabase/functions/send-refund-confirmation/index.ts | 退款確認 email 已實作 |
| 缺的只是「approveCancellation 呼叫 newebpay-refund」這個 wire | 本 spec 要做的事 |
2026-05-17 dogfood spec · commit 3dd61a5f 含 Bug 1+4 mitigation · Bug 2/3 等老闆 confirm Option 才 ship