我正在 Node.js 应用程序中开发一项功能,需要将从 JWT(JSON Web 令牌)获取的用户 ID 与成功付款后来自 PayPal 的
custom_id
相关联。用户 ID 和 custom_id
在我的系统中具有不同的用途:用户 ID 标识登录用户,而 custom_id
是在付款成功后由 PayPal 生成的。我的目标是将每笔交易的这两个标识符存储在我的数据库中,以便将订阅付款直接链接到用户帐户。
我设想的流程如下:
custom_id
(例如“I-YXA1XYR62EKE”)。custom_id
以及用户 ID,并将两者存储在我的数据库中,从而将付款与用户关联起来。以下是我目前处理身份验证和付款启动的方式。然而,在付款成功后,我一直坚持有效地捕获来自 PayPal 的
custom_id
并将其与数据库中的用户 ID 链接起来。
中间件/ auth.js
const jwt = require("jsonwebtoken");
const auth = async (req, res, next) => {
try {
const token = req.header("x-auth-token");
if (!token)
return res.status(401).json({ msg: "No auth token, access denied." });
const verified = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
if (!verified)
return res
.status(401)
.json({ msg: "Token verification failed, authorization denied." });
req.user = verified.id;
req.token = token;
next();
} catch (e) {
res.status(500).json({ error: e.message });
}
};
module.exports = auth;
routes/paypal.js
paypalRouter.post("/create-subscription", auth, async (req,res) => {
try {
const userId = req.user;
const subscriptionDetails = {
plan_id: process.env.PAYPAL_SANDBOX_BUSSINESS_SUBSCRIPTION_PLAN_ID,
custom_id: userId,
application_context: {
brand_name: "brand.com",
return_url: "http://localhost:3001/paypal/subscriptions/webhook",
cancel_url: "http://localhost:3001/paypal/cancel-subscription",
}
};
const params = new URLSearchParams();
params.append("grant_type", "client_credentials");
const authResponse = await axios.post(
"https://api-m.sandbox.paypal.com/v1/oauth2/token",
params,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
auth: {
username: process.env.PAYPAL_CLIENT_ID,
password: process.env.PAYPAL_SECRET,
},
}
);
const access_token = authResponse.data.access_token;
const response = await axios.post(
"https://api-m.sandbox.paypal.com/v1/billing/subscriptions",
subscriptionDetails,
{
headers: {
Authorization: `Bearer ${access_token}`,
},
}
);
console.log("subscriptionId from PAYPAL (e.g "I-YXA1XYR62EKE")" + response.data.id);
console.error();
return res.json({ subscriptionId: response.data.id, ...response.data });
} catch (error) {
console.log(error);
return res.status(500).json("Something goes wrong");
}
});
paypalRouter.post("/subscriptions/webhook", async (req, res) => {
try {
const webhookEvent = req.body;
const verification = {
auth_algo: req.headers['paypal-auth-algo'],
cert_url: req.headers['paypal-cert-url'],
transmission_id: req.headers['paypal-transmission-id'],
transmission_sig: req.headers['paypal-transmission-sig'],
transmission_time: req.headers['paypal-transmission-time'],
webhook_id: process.env.Webhook_ID,
webhook_event: webhookEvent
};
const params = new URLSearchParams();
params.append("grant_type", "client_credentials");
const authResponse = await axios.post(
"https://api-m.sandbox.paypal.com/v1/oauth2/token",
params,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
auth: {
username: process.env.PAYPAL_CLIENT_ID,
password: process.env.PAYPAL_SECRET,
},
}
);
const accessToken = authResponse.data.access_token;
const verifyResponse = await axios.post(
"https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature",
verification,
{
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
}
);
if (verifyResponse.data.verification_status === "SUCCESS") {
if (webhookEvent.event_type === "PAYMENT.SALE.COMPLETED") {
console.log("Payment sale completed event received", webhookEvent);
const userId = webhookEvent.resource.custom_id; //
const subscriptionId = webhookEvent.resource.id; //
try {
await User.findByIdAndUpdate(userId, { subscriptionId: subscriptionId });
console.log("User subscription ID updated successfully");
} catch (error) {
console.error("Failed to update user subscription ID:", error);
return res.status(500).send("Failed to update user subscription ID.");
}
return res.status(200).send('Event processed');
}
} else {
console.log("Failed to verify webhook signature:", verifyResponse.data);
return res.status(401).send('Webhook signature verification failed');
}
} catch (error) {
console.error("Error handling webhook event:", error.response ? error.response.data : error.message);
return res.status(500).send("An error occurred while handling the webhook event.");
}
});
我不确定付款成功后捕获
custom_id
并将其与我的数据库中的用户 ID 相关联的最佳实践。具体来说,我正在寻找以下方面的指导:
custom_id
以确保付款成功。custom_id
与从 JWT 中提取的用户 ID 链接起来的最佳实践。任何在 Node.js 应用程序中处理类似场景的见解或示例将不胜感激。
PayPal 提供 custom_id(例如“I-YXA1XYR62EKE”)
这样的值通常在 PayPal 中称为订阅的
id
,而不是 custom_id
。
查看您的代码,您似乎在创建订阅时实际上将 userId 值存储在
custom_id
字段中。
paypalRouter.post("/create-subscription", auth, async (req,res) => {
try {
const userId = req.user;
这样就可以了。
当您收到网络钩子时,您已经完美地获取了这两个值:
if (webhookEvent.event_type === "PAYMENT.SALE.COMPLETED") {
console.log("Payment sale completed event received", webhookEvent);
const userId = webhookEvent.resource.custom_id; //
const subscriptionId = webhookEvent.resource.id; //
因此,您的问题询问了与某些 JWT 相关的下一步的“最佳实践”。据我所知,答案是……无论做什么都是有意义的。您拥有所有相关数据,可以用它做您想做的事。如果您仍然不确定,我想查看一个关于不清楚的地方的更具体的问题。