fix(instanda): rate limiting, UTC timestamps, and close silent-failure paths

- F-06: throttle the public create per IP (20/h) and per email (5/h) before the
  paid API call and the PII write.
- F-11: wrap the create path in try/catch - on an unexpected error fail closed for
  that submission (no quote-less entry), alert admins, keep the form working.
- F-14: abort the StartQuote when the write-ahead insert fails (no unaudited call);
  fire the failure alert even when storage is disabled (build an in-memory tx).
- F-10/F-17: treat a 2xx with a missing/empty quoteRef as ambiguous
  (possible_duplicate=1), not a hard failure - a quote may exist.
- F-07: write created_at/delivered_at/event timestamps in UTC to match the gmdate()
  retention/dedupe cutoffs; display in site-local via get_date_from_gmt.
- F-09: log (no silent return) when async record_feed_result cannot resolve a
  delivered transaction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 16:13:06 -06:00
co-authored by Claude Fable 5
parent 2b1c226d46
commit 0732dd662d
5 changed files with 127 additions and 28 deletions
@@ -158,7 +158,9 @@ final class Api_Client {
$data = is_array($data) ? $data : [];
if ($code >= 200 && $code < 300) {
$ref = isset($data['quoteRef']) ? (string) $data['quoteRef'] : null;
// An empty-string quoteRef is not usable - treat it as missing so the caller
// handles the 2xx-without-quoteRef case as ambiguous, not as a delivered quote.
$ref = isset($data['quoteRef']) && $data['quoteRef'] !== '' ? (string) $data['quoteRef'] : null;
$url = isset($data['urlSingleUse']) ? (string) $data['urlSingleUse'] : null;
return Api_Result::success($code, $ref, $url, $excerpt);
}