@x-money /核心
X Money API的TypeScript服务器SDK。创建结帐会话,收费链接帐户,发出退款和付款,并验证webhook。
零运行时依赖,仅限ESM, Node 24或更新版本。
npm install @x-money/core
快速启动
import { XMoney } from "@x-money/core";
// The key is always explicit. Read it from wherever you keep secrets; the SDK
// will not pick one up from the environment on its own.
const apiKey = process.env.XMONEY_API_KEY;
if (!apiKey) throw new Error("Missing XMONEY_API_KEY");
const xmoney = new XMoney(apiKey);
const session = await xmoney.checkouts.create({
line_items: [
{ name: "PlayStation 5", amount: 49999, currency: "USD", quantity: 1 },
],
success_url: "https://example.com/success",
failure_url: "https://example.com/failure",
reference: "order-2481",
});
// Send the customer to session.url, or hand it to @x-money/checkout in the
// browser to open it in a popup instead of redirecting.
金额是以货币最小单位表示的整数,所以 49999 是499.99美元。
资源
checkouts, exchanges, accounts, customers, payments, refunds, payouts, settlements, webhooks.
API公开的每个端点在这里都有一个方法。SDK自己的测试套件在两个方向上相互检查,因此如果在下一个版本中没有匹配的方法,就不能将端点添加到API中。
分页
可列出的资源有 list 方法。等待一页,或者 for await 走完每一页。签出会话和帐户交换在API中没有列表。
const firstPage = await xmoney.payouts.list({ limit: 100 });
for await (const payout of xmoney.payouts.list({ limit: 100 })) {
console.log(payout.id);
}
幂等性
每个POST都携带一个生成的等幂键。传递您自己的命令以确保跨进程重启的重试是安全的,这是您想要的任何由订单id驱动的重试。
await xmoney.refunds.create(
{ payment: "xmpy_...", reference: "refund-order-8471" },
{ idempotencyKey: "refund-order-8471" },
);
测试模式
有了测试钥匙, testOutcome 发送 Xm-Test-Outcome 头强制该请求发生拒绝、过期或授权错误。数量永远不会决定结果。
await xmoney.payments.create(
{ customer_id: "xmcu_...", account_id: "xmac_...", amount: 2437, currency: "USD" },
{ testOutcome: "INSUFFICIENT_FUNDS" },
);
人则
constructEvent 验证 xm-signature 头并返回一个类型化事件。它把 XMoneySignatureVerificationError 错误的签名、格式错误的报头或300秒重放窗口之外的时间戳。
import { XMoney, XMoneyUnknownEventError, XMoneySignatureVerificationError } from "@x-money/core";
app.post("/webhooks", express.raw({ type: "*/*" }), async (req, res) => {
let event;
try {
event = xmoney.webhooks.constructEvent(
req.body,
req.headers["xm-signature"],
process.env.XMONEY_WEBHOOK_SECRET,
);
} catch (err) {
if (err instanceof XMoneyUnknownEventError) return res.status(200).end();
if (err instanceof XMoneySignatureVerificationError) return res.status(400).end();
throw err;
}
if (event.type === "checkout_session_completed") {
await fulfillOrder(event.event.merchant_reference);
}
res.status(200).end();
});
req.body 必须是未解析的请求体。经过json解析和重新序列化的正文将不匹配签名。表达的需要 express.raw() 在这条路上,而不是 express.json().
无法识别的事件类型是比此包更老的真实投递。抓 XMoneyUnknownEventError 并返回200,否则将重试端点,直到老化为止。
要在测试中签署有效载荷, generateTestHeader 返回一个 xm-signature 价值 constructEvent 将接受:
const header = xmoney.webhooks.generateTestHeader(rawBody, secret);
const event = xmoney.webhooks.constructEvent(rawBody, header, secret);
constructEvent 使用 node:crypto 它是同步的。 constructEventAsync hmac -sign与Web加密(crypto.subtle)而不是 createHmac。相同的参数,相同的错误,并且它接受标头 generateTestHeader 生产。取回 Request 仍然需要 await request.text()。这个包的其余部分仍然是导入的 node:crypto 对于HTTP客户端,所以在Node上运行webhook路由,直到webhook -only导出存在。
const event = await xmoney.webhooks.constructEventAsync(
await request.text(),
request.headers.get("xm-signature"),
process.env.XMONEY_WEBHOOK_SECRET,
);
快速结账回调
结帐会话可以询问客户详细信息,并让您在客户仍在结帐时回答:
const session = await xmoney.checkouts.create({
line_items: [{ name: "Starter kit", amount: 1986, currency: "USD", quantity: 1 }],
reference: "order-2481",
requested_customer_details: ["EMAIL", "SHIPPING_ADDRESS"],
allowed_shipping_countries: ["US"],
callback_events: ["CUSTOMER_DETAILS_UPDATED", "CONFIRMATION_REQUESTED"],
});
X Money然后发送到你的回调端点,它与你的webhook端点分开注册,并有自己的回调端点 xmcbsk_ 签署的秘密。 checkout_session_customer_details_updated 当结帐打开时,当客户改变他们的送货地址或电子邮件时,到达;回答这个地址,或者拒绝它。 checkout_session_confirmation_requested 付款时到达,在任何资金流动之前;批准或拒绝订单。客户在等待你的答复,所以在5秒内回复。超时、非200状态或非决策的消息体使该步骤失败,客户可以重试。
app.post("/xmoney/callbacks", express.raw({ type: "*/*" }), (req, res) => {
let callback;
try {
callback = xmoney.callbacks.constructEvent(
req.body,
req.headers["xm-signature"],
process.env.XMONEY_CALLBACK_SECRET,
);
} catch {
return res.status(400).end();
}
const address = callback.event.customer_details?.shipping_address;
if (callback.type === "checkout_session_customer_details_updated") {
if (address && /^p\.?\s*o\.?\s*box/i.test(address.street)) {
return res.json(xmoney.callbacks.reject({ field: "SHIPPING_ADDRESS", message: "We can't ship to PO boxes" }));
}
return res.json(xmoney.callbacks.approve({ tax_line_items: [{ name: "Sales tax", amount: taxFor(address), currency: "USD" }] }));
}
res.json(xmoney.callbacks.approve());
});
approve() 没有税,会议的税不变,只有 checkout_session_customer_details_updated 接受税收。 reject() 没有字段拒绝整个订单。一个 rejected_message 显示给客户,限制为100个字符。 constructEventAsync 和 generateTestHeader 就像他们为webhook做的那样。
错误
| 错误 | 提出当 |
|---|---|
XMoneyError | 基类。每个SDK错误都是一个。 |
XMoneyMissingApiKeyError | 没有向构造函数传递API密钥。 |
XMoneyInvalidApiKeyError | 关键不是一个 xmsk_test_ / xmsk_prod_ 密钥。 |
XMoneyInvalidBaseUrlError | baseUrl 不是一个绝对URL。 |
XMoneyAPIError | API返回一个非2xx。带一个标准化的 code, HTTP status, requestId. |
XMoneyConnectionError | 该请求从未得到回应。 code 是 timeout, aborted,或者 network. |
XMoneySignatureVerificationError | 未验证webhook或回调签名,或者签名密钥类型错误(xmwhsk_ 人, xmcbsk_ 回调)。 |
XMoneyUnknownEventError | 签名已验证,但此包没有事件的类型。抓住它并返回200。 |
请求在30秒后超时,并在429秒、5xx秒和网络故障时重试两次,并伴有指数级的回退 Retry-After。两者都是可配置的,每个客户端和每个请求的超时。
const xmoney = new XMoney({
apiKey,
timeoutMs: 10_000,
maxRetries: 5,
onResponse: ({ requestId, status, method, path }) => {
console.log(method, path, status, requestId);
},
});
该包附带了一个嵌入TypeScript源的源映射。节点仅在开始时将其应用于堆栈跟踪 --enable-source-maps,或之后 process.setSourceMapsEnabled(true)。如果没有这些,SDK内部的跟踪就会指向缩小的bundle。
关于int64的注释
API将64位整数序列化为JSON字符串,因为JavaScript数字无法保存较大的值。SDK在输出时将已知的int64字段转换回数字,因此数量和时间戳将如您所期望的那样到达。
metadata 跳过,因此永远不会重写您自己的数据。
文档
指南、API参考和webhook目录在 xmoney.mintlify.site /文档.
让AI编码代理使用此包收集付款:
npx skills add https://xmoney.mintlify.site/docs
浏览器是SDK的一半, @x-money /结帐,呈现Pay with X按钮并打开此包创建的会话。
支持
报告错误并提出问题 X帮助中心。安全问题也是如此;请不要公开发布。
许可证
麻省理工学院。全文在包中作为 LICENSE.