กระทบยอด X/V/A และส่งออก
หน้านี้เป็นเครื่องมือปฏิบัติการใน Back Office สำหรับหาเงินที่ยังไม่ลงบัญชีตรงกัน และส่งออก snapshot เดิมไปตรวจต่อ โดยไม่เปิดเผยข้อมูลบัญชีธนาคาร ผู้ให้บริการ เส้นทางสภาพคล่อง หรือที่อยู่ object storage
พร้อมสำหรับขั้นตอน deploy
สัญญา DB, HTTP, UI และ worker ของ reconciliation.read, export.create และ export.auth.step-up ผ่าน release gates ใน repository และตั้งเป็น serverBacked: true พร้อมกันแล้ว เมนูจะปรากฏเฉพาะหลังผู้ดูแล deploy ชุดนี้และ authoritative bootstrap ให้ capability/API contract ครบ; สถานะนี้ไม่ได้หมายความว่ามีการ deploy remote แล้ว
ใครเห็นข้อมูลอะไร
| Scope ปัจจุบัน | self | subtree |
|---|---|---|
| White Label | ตัวเอง | ตัวเองและสายภายใต้ White Label เดียวกัน |
| Agent | ตัวเอง | ตัวเองและ Merchant ของ Agent นั้น |
| Merchant | ตัวเอง | ไม่อนุญาต |
สิทธิ์หน้าอ่านคือ reconciliation:read พร้อม API contract reconciliation ส่วนการสร้างไฟล์ต้องมี exports:create, contract exports และสิทธิ์อ่าน dataset ต้นทางด้วย การมีเมนูหรือ role label ใน browser ไม่ใช่หลักฐานอนุญาต; ฐานข้อมูลตรวจสมาชิก, hierarchy, environment และ scope ซ้ำทุก request และคืน 404 แบบเดียวกันเมื่อ target ไม่อยู่ในสาย
ความหมายของ X, V และ A
X = ordered_minor ยอดที่สร้างรายการ
V = transferred_minor ยอดโอนจริงจากหลักฐานที่เชื่อถือได้
A = accepted_minor ยอดที่ระบบรับ
net = A - fee| กรณี | กติกา |
|---|---|
| โอนตรง | V=X, รับ A=X |
| โอนไม่ถึง | รับตามยอดจริง A=V |
| โอนเกิน | รับไม่เกินยอดที่สร้าง A=X, ปิดงาน และติด action_required=contact_support |
| ยังไม่มีหลักฐาน | V=null, A=fee=net=0 และคงสถานะ pending |
| ต้องตัดสินโดยเจ้าหน้าที่ | แสดง manual review โดยไม่ตีความว่า balanced |
ส่วนเกินจาก overpayment ไม่สร้างเครดิตอัตโนมัติ ผู้ใช้งานต้องติดต่อ Support หลังบ้านตาม flag ของรายการนั้น
balanced หมายถึงยอด accepted, fee และ net ตรงกับ immutable ledger ทั้งหมด ไม่ได้หมายถึงแค่ V=X ส่วน variance ต้องมีผลต่าง ledger อย่างน้อยหนึ่งค่า ระบบตรวจสมการเงินซ้ำใน response adapter และใช้ decimal string หน่วยสตางค์เพื่อไม่เสีย precision
Snapshot และหน้าถัดไป
หน้าแรกเรียก:
GET /v1/bo/reconciliation?scope_id={scope}&livemode=true¤cy=THB&view=self&from={UTC}&to={UTC}&limit=50
X-PG-BO-Client: 1ช่วงเวลาต้องมากกว่า 0 ไม่เกิน 31 วัน และไม่เป็นอนาคต หน้าแรกห้ามส่ง cursor หรือ as_of; PostgreSQL เป็นผู้ตรึง as_of หลังตรวจสิทธิ์แล้ว หน้าถัดไปต้องส่งทั้ง cursor และ as_of เดิม การเปลี่ยน scope, mode, ช่วงเวลา, status, resolution หรือ limit ทำให้ cursor เดิมใช้ไม่ได้
ลำดับคงที่คือ (occurred_at DESC, reconciliation_ref DESC) และข้อมูลที่เกิดหลัง as_of จะไม่แทรกเข้ามาใน snapshot เดิม
สร้าง CSV แบบ asynchronous
ปุ่มส่งออกสร้าง snapshot ของ dataset ปัจจุบันหนึ่งชุด:
reconciliation— X/V/A, fee, net และผลต่าง ledger;statement— posting จาก immutable ledger; หรือfinancial_report— volume และ economics ตาม scope
column set เป็นแบบตายตัวและ row_limit สูงสุด 100,000 แถว ถ้าเกินจะจบด้วย row_limit_exceeded; ระบบไม่สร้างไฟล์ที่ตัดข้อมูลเงียบ ๆ ไฟล์อยู่ใน dedicated private R2_EXPORTS ที่ bind เฉพาะ Money และมี bucket-wide lifecycle ไม่เกิน 7 วัน จึงไม่แชร์ capability กับ Edge Web/QR/statement และไม่มีชื่อ, อีเมล, เบอร์โทร, เลขบัญชี, raw evidence, metadata หรือข้อมูล routing ภายใน
การสร้างไฟล์เปิด OIDC popup ซึ่งส่ง callback ชนิด paygate:export-step-up และ proof ผูกกับ method, path, scope, livemode, body digest, idempotency digest, query_digest และ column_set_hash หลังยืนยันแล้ว browser ส่งคำสั่งเดิมไปที่:
POST /v1/bo/exports
If-Match: "0"
Idempotency-Key: {stable-key}
X-PG-Step-Up-Token: pgsea1.{opaque}
X-PG-BO-Client: 1
Content-Type: application/json; charset=utf-8คำตอบ queued หรือ processing เป็นสถานะจริง ไม่ใช่ fake success การส่ง idempotency key เดิมพร้อม body เดิมใช้ดู job เดิมได้โดยไม่สร้าง snapshot ซ้ำ
ดาวน์โหลดโดยไม่ให้ grant หลุดเข้า HTTP metadata
job ที่ ready คืน handoff อายุไม่เกิน 5 นาที:
{
"scheme": "pg_opaque_grant_v1",
"method": "POST",
"path": "/v1/bo/export-downloads:consume",
"one_time": true,
"grant": "{opaque-base64url}",
"issued_at": "{canonical-UTC}",
"expires_at": "{canonical-UTC}"
}UI ล้าง handoff ออกจาก global/reactive state ก่อนเรียก download helper และห้ามนำ grant ไปใส่ href, URL, query หรือ header จากนั้น helper ส่ง body bytes เพียงรูปแบบเดียว:
POST /v1/bo/export-downloads:consume
Accept: text/csv
Content-Type: application/json
X-PG-BO-Client: 1
{"grant":"{opaque-base64url}"}request ใช้ same-origin cookie, Origin และ Fetch Metadata เดิม และห้ามตาม redirect คำตอบสำเร็จต้องเป็น 200, text/csv; charset=utf-8, gzip, attachment filename ลงท้าย .csv, private, no-store, nosniff, no-referrer และ CSP default-src 'none'; base-uri 'none'; frame-ancestors 'none' เท่านั้น ไม่มี ETag, Digest, R2 locator หรือ redirect
grant ใช้ได้ครั้งเดียว 404 ครอบคลุมกรณีใช้แล้ว, หมดอายุ, ผิดผู้ใช้, ผิด scope หรือ session ไม่ active โดยไม่บอกว่ากรณีใด 502 คือ integrity/header mismatch และ UI ไม่ retry 404 หรือ 502 อัตโนมัติ
ก่อนเปิด feature
ต้องผ่าน fresh migration replay และ pgTAP ของ 0083/0084, DB/API/worker/Money/Admin tests, checksum/expiry ของ bucket ส่วนตัว R2_EXPORTS ซึ่ง bind เฉพาะ Money, one-time consume race, Tail native Workers Logs redaction, contract/OpenAPI drift, UI tests/build/ZIP และ staging smoke พร้อมกัน หลังผ่านแล้วจึงเปลี่ยน readiness ทั้งสาม operation เป็น true และ rebuild White Label artifacts
