Commit f19658e1 by tdgiang

Require CMND/CCCD on transaction creation and enforce amount range 1M-20M

Sổ Bán Lẻ rejects customer creation with a 422 ("Vui lòng nhập số
CMND/CCCD") when id_card_number is null - discovered via manual E2E
testing against the real service. Add a required customerIdCard field to
the admin form and controller, persisted on AdminTransaction, and pass
through as customer_data.id_card_number instead of null.

Also add the requested amount validation: payment amount must be
strictly greater than 1,000,000 and less than 20,000,000, enforced both
client-side (form) and server-side (createTransaction).
Co-Authored-By: 's avatarClaude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KPzWwuTEeX2vXGXvyGn4q
parent 6536143a
......@@ -18,14 +18,20 @@ exports.createTransaction = function (req, res) {
var customerName = req.body.customerName;
var customerPhone = req.body.customerPhone;
var customerAddress = req.body.customerAddress;
var customerIdCard = req.body.customerIdCard;
var rawAmount = req.body.amount;
if (!customerName || !customerPhone || !customerAddress || !rawAmount) {
if (!customerName || !customerPhone || !customerAddress || !customerIdCard || !rawAmount) {
return res.status(400).json({ code: "99", data: "MISSING_FIELDS" });
}
var targetAmount = parseInt(rawAmount, 10);
if (!isFinite(targetAmount) || targetAmount <= 0 || String(targetAmount) !== String(rawAmount).trim()) {
if (
!isFinite(targetAmount) ||
String(targetAmount) !== String(rawAmount).trim() ||
targetAmount <= 1000000 ||
targetAmount >= 20000000
) {
return res.status(400).json({ code: "99", data: "INVALID_AMOUNT" });
}
......@@ -36,7 +42,7 @@ exports.createTransaction = function (req, res) {
tax_no: null,
name: customerName,
address: customerAddress,
id_card_number: null,
id_card_number: customerIdCard,
passport_number: null,
email: null,
};
......@@ -67,6 +73,7 @@ exports.createTransaction = function (req, res) {
customerName: customerName,
customerPhone: customerPhone,
customerAddress: customerAddress,
customerIdCard: customerIdCard,
amount: amount,
sobanleOrderId: String(order.orderId),
merchantToken: merchantToken,
......
......@@ -9,6 +9,7 @@ var AdminTransactionSchema = new Schema(
customerName: { type: String, required: true },
customerPhone: { type: String, required: true },
customerAddress: { type: String, required: true },
customerIdCard: { type: String, required: true },
amount: { type: Number, required: true },
payType: { type: String, default: "DC" },
status: {
......
......@@ -396,23 +396,31 @@
Vui lòng nhập địa chỉ
</p>
</div>
<div class="field" data-field="customerIdCard">
<label for="customerIdCard">Số CMND/CCCD</label>
<input type="text" id="customerIdCard" name="customerIdCard" placeholder="012345678901" autocomplete="off" inputmode="numeric" required aria-describedby="customerIdCard-error">
<p class="field-error" id="customerIdCard-error" role="alert">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Vui lòng nhập số CMND/CCCD
</p>
</div>
<div class="field" data-field="amount">
<label for="amount">Số tiền thanh toán</label>
<div class="amount-field">
<input type="number" id="amount" name="amount" placeholder="0" min="1000" step="1000" inputmode="numeric" required aria-describedby="amount-error amount-preview">
<input type="number" id="amount" name="amount" placeholder="0" min="1000001" max="19999999" step="1" inputmode="numeric" required aria-describedby="amount-error amount-preview">
<span class="amount-suffix">VNĐ</span>
</div>
<p class="field-error" id="amount-error" role="alert">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Số tiền phải từ 1,000 VNĐ trở lên
Số tiền phải lớn hơn 1,000,000 và nhỏ hơn 20,000,000 VNĐ
</p>
<p class="amount-preview" id="amount-preview" aria-live="polite"></p>
<div class="amount-presets" role="group" aria-label="Chọn nhanh số tiền">
<button type="button" class="chip" data-amount="100000">100,000</button>
<button type="button" class="chip" data-amount="200000">200,000</button>
<button type="button" class="chip" data-amount="500000">500,000</button>
<button type="button" class="chip" data-amount="1000000">1,000,000</button>
<button type="button" class="chip" data-amount="2000000">2,000,000</button>
<button type="button" class="chip" data-amount="5000000">5,000,000</button>
<button type="button" class="chip" data-amount="10000000">10,000,000</button>
<button type="button" class="chip" data-amount="15000000">15,000,000</button>
<button type="button" class="chip" data-amount="19000000">19,000,000</button>
</div>
</div>
<button type="submit" id="submitBtn">
......@@ -474,12 +482,13 @@
if (!data.customerName.trim()) { setFieldInvalid('customerName'); valid = false; } else { setFieldValid('customerName'); }
if (!validatePhone(data.customerPhone)) { setFieldInvalid('customerPhone'); valid = false; } else { setFieldValid('customerPhone'); }
if (!data.customerAddress.trim()) { setFieldInvalid('customerAddress'); valid = false; } else { setFieldValid('customerAddress'); }
if (!data.customerIdCard.trim()) { setFieldInvalid('customerIdCard'); valid = false; } else { setFieldValid('customerIdCard'); }
var amountNum = parseInt(data.amount, 10);
if (!isFinite(amountNum) || amountNum < 1000) { setFieldInvalid('amount'); valid = false; } else { setFieldValid('amount'); }
if (!isFinite(amountNum) || amountNum <= 1000000 || amountNum >= 20000000) { setFieldInvalid('amount'); valid = false; } else { setFieldValid('amount'); }
return valid;
}
['customerName', 'customerAddress'].forEach(function (id) {
['customerName', 'customerAddress', 'customerIdCard'].forEach(function (id) {
document.getElementById(id).addEventListener('input', function () {
setFieldValid(id);
});
......@@ -495,6 +504,7 @@
customerName: form.customerName.value,
customerPhone: form.customerPhone.value,
customerAddress: form.customerAddress.value,
customerIdCard: form.customerIdCard.value,
amount: form.amount.value
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment