Thêm widget giá vàng trực tiếp vào WordPress
Thêm widget giá vàng trực tiếp miễn phí vào WordPress bằng một iframe. Dùng được với Gutenberg và Elementor, không cần API key hay plugin.
Đọc →Xây máy tính giá trang sức từ giá vàng trực tiếp theo gram và tuổi vàng, với phép tính thập phân chính xác, cache, biên lợi nhuận và giới hạn định giá rõ ràng.
Tiệm vàng, cửa hàng cầm đồ và người bán thương mại điện tử đều cần một công cụ nhỏ giống nhau: nhập trọng lượng và tuổi vàng để nhận giá trị vật liệu. Bài này xây công cụ đó từ đầu đến cuối bằng JavaScript thuần, từ giá trực tiếp theo gram đến giao diện trong trình duyệt.
Kết quả là giá trị vật liệu: giá trị phần vàng trong món đồ theo giá spot hiện tại, trước khi cộng các chi phí khác. Đây không phải kết quả thẩm định, giá bán lại được bảo đảm hay số tiền người mua chắc chắn trả.
Các phần không được tính:
Mỗi kết quả nên đi kèm dòng giải thích này. Hãy đặt phần giới hạn ngay trong giao diện, không chỉ trong tài liệu.
Endpoint /v1/carat trả về giá theo gram cho tám độ tinh khiết phổ biến trong một lượt gọi và không cần API key:
GET https://api.goldprice.dev/v1/carat?currency=USD
Một phản hồi thực tế có dạng:
{
"currency": "USD",
"timestamp": "2026-07-16T18:12:06.394451Z",
"price_gram_24k": "128.24",
"price_gram_22k": "117.55",
"price_gram_21k": "112.21",
"price_gram_20k": "106.86",
"price_gram_18k": "96.18",
"price_gram_16k": "85.49",
"price_gram_14k": "74.80",
"price_gram_10k": "53.43"
}
Hai chi tiết quan trọng cho đoạn mã bên dưới. Mỗi trường giá là một chuỗi, không phải số, để giữ nguyên các chữ số thập phân sau vòng chuyển đổi JSON. Tham số currency chấp nhận mọi đồng tiền trong 31 đồng tiền API hỗ trợ, nên cùng endpoint có thể dùng cho USD, EUR, INR hoặc IDR bằng cách đổi một query parameter.
Dữ liệu nguồn cập nhật theo nhịp tương tự, vì vậy gọi lại ở mỗi lượt tải trang chỉ làm tăng độ trễ. Đặt một máy chủ Node nhỏ với cache trong bộ nhớ ở phía trước:
import { createServer } from "node:http";
const UPSTREAM = "https://api.goldprice.dev/v1/carat";
const TTL_MS = 60_000;
const cache = new Map(); // currency -> { data, fetchedAt }
async function fetchCarat(currency) {
const res = await fetch(`${UPSTREAM}?currency=${encodeURIComponent(currency)}`);
if (!res.ok) {
throw new Error(`upstream ${res.status}`);
}
const data = await res.json();
if (typeof data.price_gram_24k !== "string") {
throw new Error("malformed upstream response");
}
return data;
}
async function getCarat(currency) {
const cached = cache.get(currency);
const now = Date.now();
if (cached && now - cached.fetchedAt < TTL_MS) {
return cached.data;
}
try {
const data = await fetchCarat(currency);
cache.set(currency, { data, fetchedAt: now });
return data;
} catch (err) {
if (cached) {
return cached.data; // stale-if-error: serve last known value
}
throw err;
}
}
const server = createServer(async (req, res) => {
const url = new URL(req.url, "http://localhost");
if (url.pathname !== "/api/carat") {
res.writeHead(404).end("not found");
return;
}
const currency = (url.searchParams.get("currency") || "USD").toUpperCase();
try {
const data = await getCarat(currency);
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(data));
} catch (err) {
res.writeHead(502, { "content-type": "application/json" }).end(
JSON.stringify({ error: "carat_unavailable" })
);
}
});
server.listen(3001);
Đây là node:http thuần, không cần framework cho một endpoint. Xử lý lỗi quan trọng không kém đường chạy thành công: phản hồi khác 200 hoặc body sai định dạng sẽ ném lỗi, và getCarat quay về giá trị cache gần nhất nếu có. Chỉ khi hoàn toàn không có cache, client mới nhận lỗi 502.
Không gọi api.goldprice.dev trực tiếp từ JavaScript trong trình duyệt. API không mở CORS cho trình duyệt, nên fetch phía client sẽ thất bại. Hãy chuyển mọi yêu cầu qua endpoint phía máy chủ của bạn. Cách này cũng giúp mọi người dùng chung một cache thay vì mỗi tab trình duyệt giữ một bản riêng.
Công thức chính chỉ có một phép nhân: weight_grams × per_gram_karat_price. Hai toán hạng cần giữ số thập phân chính xác vì phép tính dấu phẩy động với tiền có thể sai lệch:
console.log(0.1 + 0.2); // 0.30000000000000004
API đã trả về chuỗi thập phân chính xác. Hãy giữ giá trị dưới dạng số nguyên theo đơn vị tiền nhỏ nhất thay vì chuyển sang Number rồi chuyển ngược:
function toMinorUnits(decimalString, decimals = 2) {
const [whole, frac = ""] = decimalString.split(".");
const paddedFrac = (frac + "0".repeat(decimals)).slice(0, decimals);
const sign = whole.startsWith("-") ? -1n : 1n;
const wholeAbs = whole.replace("-", "");
return sign * (BigInt(wholeAbs) * 10n ** BigInt(decimals) + BigInt(paddedFrac || "0"));
}
function fromMinorUnits(minorUnits, decimals = 2) {
const negative = minorUnits < 0n;
const abs = negative ? -minorUnits : minorUnits;
const divisor = 10n ** BigInt(decimals);
const whole = abs / divisor;
const frac = (abs % divisor).toString().padStart(decimals, "0");
return `${negative ? "-" : ""}${whole}.${frac}`;
}
function materialValue(weightGrams, perGramPriceString) {
const priceMinor = toMinorUnits(perGramPriceString, 2); // cents
const weightMilligrams = BigInt(Math.round(weightGrams * 1000)); // 3dp precision
const totalMinor = (priceMinor * weightMilligrams) / 1000n;
return fromMinorUnits(totalMinor, 2);
}
Không cần thư viện số lớn, chỉ dùng BigInt có sẵn trong các runtime JavaScript hiện nay. Trọng lượng ban đầu là số thực vì cân kim hoàn thường đọc đến 0,01g hoặc 0,001g. Đoạn mã chuyển nó thành số nguyên miligram trước khi nhân, rồi chia ở bước cuối để giữ độ chính xác của giá trị trung gian. Phép chia số nguyên sẽ cắt phần dư thay vì làm tròn, vì vậy hãy chọn quy tắc làm tròn rõ ràng theo đơn vị tiền tệ trước khi đưa vào sản xuất.
Giá trị vật liệu ở bước 3 được suy ra từ giá spot, chưa phải báo giá của cửa hàng. Cần thêm hai đầu vào dưới dạng hai dòng riêng, không trộn vào giá spot:
function quoteBreakdown(materialValueMinor, marginPercent, makingChargeMinor) {
const marginMinor = (materialValueMinor * BigInt(Math.round(marginPercent * 100))) / 10000n;
return {
material: materialValueMinor,
margin: marginMinor,
makingCharge: makingChargeMinor,
total: materialValueMinor + marginMinor + makingChargeMinor,
};
}
Ba trường riêng giúp phép tính có thể kiểm tra được. Khách hàng hoặc cơ quan quản lý có thể thấy giá trị vật liệu từ giá spot trên một dòng, tách khỏi phần cửa hàng tự cộng thêm.
Phía trình duyệt gọi máy chủ của bạn, không gọi API nguồn:
async function loadCarat(currency) {
const res = await fetch(`/api/carat?currency=${encodeURIComponent(currency)}`);
if (!res.ok) {
throw new Error(`carat request failed: ${res.status}`);
}
return res.json();
}
function estimateValue(weightGrams, karat) {
const karatKey = `price_gram_${karat}k`;
return loadCarat("USD").then((data) => {
const perGram = data[karatKey];
if (!perGram) {
throw new Error(`unsupported karat: ${karat}`);
}
return {
material: materialValue(weightGrams, perGram),
timestamp: data.timestamp,
currency: data.currency,
};
});
}
document.getElementById("calc-btn").addEventListener("click", () => {
const grams = Number(document.getElementById("weight").value);
const karat = Number(document.getElementById("karat").value);
estimateValue(grams, karat)
.then((result) => {
document.getElementById("result").textContent =
`${result.material} ${result.currency} as of ${result.timestamp}`;
})
.catch((err) => {
document.getElementById("result").textContent =
"Price unavailable right now. Try again shortly.";
console.error(err);
});
});
Mỗi kết quả trên giao diện cần hiển thị dấu thời gian và tiền tệ. Bên dưới cần có câu cố định: đây là ước tính giá trị vật liệu, không phải thẩm định, và chưa gồm đá, công chế tác, thuế cùng tình trạng món đồ. Đặt câu này trực tiếp trong template, không giấu trong tooltip.
Lỗi ở phía máy chủ có ba dạng và đoạn mã trên xử lý cả ba:
fetchCarat bắt lỗi và quay về cachetypeof data.price_gram_24k !== "string" chặn dữ liệu sai trước khi cache hoặc trả vềKhi có cache hợp lệ, người dùng vẫn nhận giá trị tốt gần nhất cùng dấu thời gian thật. Khi không có cache dùng được, client nhận trạng thái lỗi rõ ràng thay vì phép tính sai hoặc số 0 im lặng.
Endpoint ẩn danh trong bài phù hợp cho phát triển và thử nghiệm. Dùng dữ liệu API thô trong sản phẩm thương mại hoặc hoạt động tạo doanh thu cần giấy phép gói Pro trở lên. Nếu bạn chỉ cần hiển thị giá trực tiếp trên trang mà không tính toán từ dữ liệu thô, widget nhúng chính thức được dùng miễn phí cho hiển thị thương mại và không cần API key.
hướng dẫn liên quan
Thêm widget giá vàng trực tiếp miễn phí vào WordPress bằng một iframe. Dùng được với Gutenberg và Elementor, không cần API key hay plugin.
Đọc →Build a live India gold-rate app from INR per-gram 24K and 22K prices, with server-side caching, Indian number formatting, GST estimates, and clear limits.
Đọc →Build a reproducible local-currency gold backtest from settled XAU/USD bars and dated FX observations, without inserting future rates into historical rows.
Đọc →goldprice.dev
Giá vàng trực tiếp, dữ liệu OHLC lịch sử và tổng hợp nhiều nguồn qua REST và SSE.