
本文详解如何利用 Paystack 的 metadata 字段,在前端表单中收集并安全传递用户首名、姓氏和收货地址,并在后端验证接口中正确解析存储,突破默认仅支持 email 和 amount 的限制。
本文详解如何利用 paystack 的 `metadata` 字段,在前端表单中收集并安全传递用户首名、姓氏和收货地址,并在后端验证接口中正确解析存储,突破默认仅支持 email 和 amount 的限制。
Paystack 官方 SDK 默认仅将 email 和 amount 映射为交易基础字段,若需持久化保存客户姓名(first_name/last_name)或收货地址(shipping address)等业务关键信息,必须使用 metadata 字段——这是 Paystack 专为开发者设计的灵活扩展机制,允许你以结构化方式附加任意自定义数据,并在后续回调或验证响应中完整获取。
✅ 正确配置前端:将表单字段注入 metadata
Paystack 的 PaystackPop.setup() 方法不直接接受 first_name、last_name 或 address 等顶层参数(这些字段会被忽略)。你必须将其包裹在 metadata.custom_field 数组中。以下是修正后的 JavaScript 代码:
function payWithPaystack(e) {
e.preventDefault();
const firstName = document.getElementById("first-name").value.trim();
const lastName = document.getElementById("last-name").value.trim();
const shippingAddress = document.getElementById("shipping-address").value.trim();
const email = document.getElementById("email-address").value;
const amount = parseInt(document.getElementById("amount").value) * 100; // 转为 kobo
let handler = PaystackPop.setup({
key: 'pk_test_xxxxxxxxxx', // 替换为你的公钥
email: email,
amount: amount,
ref: 'CLE-BPS' + Math.floor(Math.random() * 1000000000 + 1),
// ✅ 关键修改:所有自定义字段必须置于 metadata.custom_field 数组内
metadata: {
custom_fields: [ // 注意:Paystack 文档推荐使用 custom_fields(复数),兼容性更佳
{
display_name: "Customer Name",
variable_name: "full_name",
value: `${firstName} ${lastName}`
},
{
display_name: "Shipping Address",
variable_name: "shipping_address",
value: shippingAddress
}
]
},
onClose: function() {
alert('Transaction cancelled.');
window.location = "https://my-url/?transaction=cancelled";
},
callback: function(response) {
alert('Payment complete! Reference: ' + response.reference);
window.location = "https://my-url/verify_transaction.php?reference=" + response.reference;
}
});
handler.openIframe();
}⚠️ 注意事项:
- custom_fields(复数)是 Paystack 官方文档推荐写法,比 custom_field 更稳定;若使用旧版 SDK,请确认其兼容性。
- 每个对象中 display_name 将显示在 Paystack 后台交易详情页,variable_name 是你在后端提取时的逻辑标识,value 为实际值。
- 建议对输入做 .trim() 处理,避免空格干扰;金额务必转为整数(单位:kobo)。
✅ 后端解析:从 verify 接口安全提取 metadata
在 verify_transaction.php 中,Paystack 返回的 metadata 会嵌套在 data.metadata.custom_fields 下。你需要在 json_decode() 后精准定位:
// ... cURL 验证逻辑(保持不变)
$response = curl_exec($curl);
$result = json_decode($response);
if ($result->status === true && $result->data->status === 'success') {
// ✅ 从 metadata.custom_fields 提取自定义字段
$customFields = $result->data->metadata->custom_fields ?? [];
$firstName = '';
$lastName = '';
$shippingAddress = '';
foreach ($customFields as $field) {
switch ($field->variable_name) {
case 'full_name':
$names = explode(' ', trim($field->value), 2);
$firstName = $names[0] ?? '';
$lastName = $names[1] ?? '';
break;
case 'shipping_address':
$shippingAddress = $field->value;
break;
}
}
$fullname = trim($firstName . ' ' . $lastName);
$customer_email = $result->data->customer->email;
$customer_id = $result->data->customer->id;
$amount = $result->data->amount / 100; // 转回 Naira
$date = date('Y-m-d H:i:s');
// ✅ 插入数据库(注意:使用预处理语句防止 SQL 注入)
$stmt = $link->prepare(
"INSERT INTO transactions (status, reference, fullname, first_name, last_name, amount, customer_email, shipping_address, customer_id, date_purchased)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
$stmt->bind_param(
"sssssdssis",
$result->data->status,
$result->data->reference,
$fullname,
$firstName,
$lastName,
$amount,
$customer_email,
$shippingAddress,
$customer_id,
$date
);
if ($stmt->execute()) {
header("Location: https://my-url/success.php?status=success");
} else {
error_log("DB Insert Failed: " . $stmt->error);
header("Location: error.php");
}
$stmt->close();
} else {
header("Location: error.php");
}? 调试建议
- 在 verify_transaction.php 中临时添加 echo '
'; print_r($result); exit; 可直观查看完整响应结构,确认 metadata 所在路径;
- Paystack Dashboard → Transactions → 点击对应交易 → 查看 “Metadata” 标签页,验证数据是否成功提交;
- 生产环境务必使用 HTTPS,且 SECRET_KEY 严禁暴露在前端或日志中。
通过以上配置,你即可在 Paystack 支付流程中完整捕获、传输并落库用户姓名与地址信息,为订单管理、CRM 同步及个性化服务提供可靠数据基础。

















