Spaces:
Running
Running
Download src/controllers/webhook.controller.js from AndreaNotes1/auth-app-api: direct link, hf CLI and curl.
- Browser
- Download file 3.1 kB
-
https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/controllers/webhook.controller.js
- Command line
-
hf download hf://spaces/AndreaNotes1/auth-app-api/src/controllers/webhook.controller.js
-
curl -L -o webhook.controller.js https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/controllers/webhook.controller.js
3.1 kB
| const Order = require("../models/Order"); | |
| const payoneerCheckout = require("../utils/payoneerCheckout"); | |
| const { confirmOrderAndGrantAccess } = require("../utils/orderConfirmation"); | |
| const logger = require("../utils/logger"); | |
| // PAYONEER-DOCS-NEEDED: confirm the actual header name Payoneer sends | |
| // the webhook signature in β this is a placeholder guess following | |
| // common convention (e.g. Stripe uses "Stripe-Signature"). | |
| const SIGNATURE_HEADER = "x-payoneer-signature"; | |
| /** | |
| * Receives Payoneer Checkout's payment-status webhook and, on a | |
| * successful payment, automatically confirms the matching Order and | |
| * grants the buyer access β no admin involved, unlike the manual | |
| * bank_wire/payoneer-reference flow. | |
| * | |
| * Registered in app.js with a raw body parser (see | |
| * routes/webhook.routes.js) so `req.body` here is the exact raw | |
| * bytes Payoneer signed, not the parsed JSON the rest of the app uses. | |
| * | |
| * Always responds 200 once the webhook is understood (even for a | |
| * "not our order" or "already confirmed" case) so Payoneer doesn't | |
| * keep retrying a webhook we've already handled. Only a genuinely bad | |
| * signature or unreadable payload gets a 4xx. | |
| */ | |
| async function handlePayoneerWebhook(req, res) { | |
| let parsed; | |
| try { | |
| parsed = payoneerCheckout.verifyAndParseWebhook({ | |
| rawBody: req.body, | |
| signatureHeader: req.headers[SIGNATURE_HEADER], | |
| }); | |
| } catch (err) { | |
| logger.error("Payoneer webhook rejected", { error: err.message }); | |
| return res.status(err.status || 400).json({ error: "Invalid webhook." }); | |
| } | |
| try { | |
| if (parsed.status !== "paid") { | |
| // Payoneer also sends non-success events (failed, cancelled, | |
| // pending, etc.) β nothing to do for those except acknowledge. | |
| logger.info("Payoneer webhook received (non-paid status, no action)", { status: parsed.status }); | |
| return res.status(200).json({ received: true }); | |
| } | |
| const order = await Order.findOne({ providerSessionId: parsed.sessionId, paymentMethod: "payoneer_checkout" }); | |
| if (!order) { | |
| logger.error("Payoneer webhook: no matching order for session", { sessionId: parsed.sessionId }); | |
| // Still 200 β this is Payoneer's event, not a request we want retried forever. | |
| return res.status(200).json({ received: true, matched: false }); | |
| } | |
| const { alreadyConfirmed } = await confirmOrderAndGrantAccess({ orderId: order._id, confirmedBy: null }); | |
| logger.info("Payoneer webhook: order auto-confirmed", { | |
| orderId: order._id.toString(), | |
| alreadyConfirmed, | |
| }); | |
| res.status(200).json({ received: true, matched: true }); | |
| } catch (err) { | |
| logger.error("Payoneer webhook processing failed", { error: err.message }); | |
| // 200 anyway: a bug on our side confirming the order shouldn't | |
| // cause Payoneer to hammer us with retries for an event we did | |
| // successfully authenticate and understand. This case should | |
| // instead be caught by monitoring your server logs. | |
| res.status(200).json({ received: true, error: "processing_failed" }); | |
| } | |
| } | |
| module.exports = { handlePayoneerWebhook }; | |