Zhou
Zhou MingxuanHead of R&D
Quality gate blocked
  • 2 critical findings exceed the limit of 0

Payment refund callback connects to third-party gateways

hanpay-apifeat/refund-callbackZhou Mubai · 2026-06-05 14:23
Changed files(2)
src/payment/refundCallback.ts+102
11 export async function handleRefundCallback(req: Request) {
2- const { orderId, amount } = req.body;
2+ const { orderId, amount, sign } = req.body;
A
AI reviewerAICritical
Missing idempotency: the payment gateway may retry a callback, allowing the same orderId to trigger duplicate refunds or notifications. Check the current order state and enforce a unique callback identifier before processing.
3+ // Verify the signature
4+ if (!verifySign(req.body, sign)) {
5+ return { ok: false, msg: "Signature validation failed" };
6+ }
A
AI reviewerAIMinor
A failed signature check should return an explicit 4xx response and emit a security alert. Returning only a business object can be ignored by the gateway.
7+ // Vulnerable: directly concatenates orderId into SQL
38 await db.query(
4- 'UPDATE orders SET status = "refunded" WHERE id = ' + orderId
9+ 'UPDATE orders SET status = "refunded", refund_amount = ' +
10+ amount +
11+ ' WHERE id = ' + orderId
A
AI reviewerAICritical
SQL injection: orderId and amount are concatenated directly into an UPDATE statement, so an attacker could inject a payload such as '1; DROP TABLE orders'. Use parameterized queries.
Suggested change
+23
11 await db.query(
2- 'UPDATE orders SET status = "refunded", refund_amount = ' +
3- amount +
4- ' WHERE id = ' + orderId
2+ 'UPDATE orders SET status = ?, refund_amount = ? WHERE id = ?',
3+ ['refunded', amount, orderId]
54 );
512 );
13+ await notifyUser(orderId);
614 return { ok: true };
715 }
A
AI reviewerAIMajor
Notification delivery is awaited without an error boundary, so a notification outage can fail an otherwise valid callback. Decouple the side effect, record failures, and retry them separately.
AI review process

Review the plan

  1. Develop a review planThis change touches refund callbacks and signature verification, so route it to Opus. Run static analysis, inspect injection and secret-handling patterns, then execute the tests.

Output

4 problems (2 errors, 2 warnings) — security/detect-object-injection, no-hardcoded-secrets

Output

2 findings: sql-injection (refundCallback.ts:11), hardcoded-secret (sign.ts:1)

Output

Tests: 6 passed, 0 failed — replay and injection scenarios are not covered
Review conclusions

Three critical findings block the quality gate: SQL injection, missing idempotency protection, and a hard-coded signing secret. Use parameterized queries, add an idempotency key, move secrets to external configuration, and then resubmit the review.

36
F
Quality score

Summary of issues

    Critical3
    Major2
    Minor1
    Info0
Lead reviewer modelClaude Opus 4.7
Code coverage72%
Review cost¥0.048