1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.talentcard.server.business.payment;
 
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
 
/**
 * Ledger for money facts that disagree with local state.
 *
 * <p>A callback or a provider query may describe a payment that local business rules refuse to
 * accept: another merchant, another amount, another order, a cancelled order or a payment whose
 * merchant binding is missing. Those cases must never be silently dropped and never auto-fulfil the
 * order, so every one of them is recorded as an open row an administrator can inspect.
 */
@Service
public class PaymentIssueRecorder {
 
    static final String PROVIDER = "HUIFU";
 
    private final JdbcTemplate jdbc;
 
    public PaymentIssueRecorder(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }
 
    public void issue(String type, String businessNo, String orderNo, String expected, String actual, long amount, String message) {
        jdbc.update("""
                insert into payment_reconciliation_issue(issue_type, provider, business_no, order_no, expected_status, actual_status, amount, detail_message)
                values (?, ?, ?, ?, ?, ?, ?, ?)
                on duplicate key update detected_at = now(), expected_status = values(expected_status), actual_status = values(actual_status), amount = values(amount), detail_message = values(detail_message)
                """, type, PROVIDER, businessNo, emptyToNull(orderNo), emptyToNull(expected), emptyToNull(actual), amount, truncate(message));
    }
 
    public void resolve(String type, String businessNo, String by) {
        jdbc.update("update payment_reconciliation_issue set status = 'resolved', resolved_at = now(), resolved_by = ? where issue_type = ? and provider = ? and business_no = ? and status = 'open'", by, type, PROVIDER, businessNo);
    }
 
    private String emptyToNull(String value) { return value == null || value.isBlank() ? null : value; }
 
    private String truncate(String value) { if (value == null) return ""; return value.length() > 500 ? value.substring(0, 500) : value; }
}