@pandait.tech/payment-nuvei 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -199,11 +199,12 @@ interface ThreeDSCallbackHandlerDeps {
|
|
|
199
199
|
logger?: Pick<Console, "log" | "error" | "warn">;
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
|
-
* Creates
|
|
203
|
-
*
|
|
202
|
+
* Creates the 3DS callback route handlers. Returns `{ POST, GET }` because the
|
|
203
|
+
* ACS may call either depending on bank/issuer.
|
|
204
204
|
*
|
|
205
|
-
* Usage:
|
|
206
|
-
*
|
|
205
|
+
* Usage (Next.js): export const { POST, GET } = create3dsCallbackHandler({ firebase: { db } });
|
|
206
|
+
* Usage (Functions): const { POST } = create3dsCallbackHandler({ firebase: { db } });
|
|
207
|
+
* exports.threeDSCallback = onRequest({ cors: true }, toCloudFunction(POST));
|
|
207
208
|
*/
|
|
208
209
|
declare function create3dsCallbackHandler(deps: ThreeDSCallbackHandlerDeps): {
|
|
209
210
|
POST: (request: Request) => Promise<Response>;
|
package/dist/handlers/index.d.ts
CHANGED
|
@@ -199,11 +199,12 @@ interface ThreeDSCallbackHandlerDeps {
|
|
|
199
199
|
logger?: Pick<Console, "log" | "error" | "warn">;
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
|
-
* Creates
|
|
203
|
-
*
|
|
202
|
+
* Creates the 3DS callback route handlers. Returns `{ POST, GET }` because the
|
|
203
|
+
* ACS may call either depending on bank/issuer.
|
|
204
204
|
*
|
|
205
|
-
* Usage:
|
|
206
|
-
*
|
|
205
|
+
* Usage (Next.js): export const { POST, GET } = create3dsCallbackHandler({ firebase: { db } });
|
|
206
|
+
* Usage (Functions): const { POST } = create3dsCallbackHandler({ firebase: { db } });
|
|
207
|
+
* exports.threeDSCallback = onRequest({ cors: true }, toCloudFunction(POST));
|
|
207
208
|
*/
|
|
208
209
|
declare function create3dsCallbackHandler(deps: ThreeDSCallbackHandlerDeps): {
|
|
209
210
|
POST: (request: Request) => Promise<Response>;
|
package/dist/handlers/index.js
CHANGED
|
@@ -916,7 +916,43 @@ function createWebhookHandler(deps) {
|
|
|
916
916
|
}
|
|
917
917
|
|
|
918
918
|
// src/handlers/3ds-callback.ts
|
|
919
|
+
function pickCres(src) {
|
|
920
|
+
for (const key of Object.keys(src)) {
|
|
921
|
+
const k = key.toLowerCase();
|
|
922
|
+
if (k === "cres" || k === "cres_value" || k === "cresvalue" || k === "value" || k === "param.value" || k === "paramvalue") {
|
|
923
|
+
const v = src[key];
|
|
924
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return src.cres || src.CRes || src.CRES || src.value || "";
|
|
928
|
+
}
|
|
929
|
+
function transStatusFromCres(cres, logger) {
|
|
930
|
+
try {
|
|
931
|
+
const base64 = cres.replace(/-/g, "+").replace(/_/g, "/");
|
|
932
|
+
const pad = base64.length % 4;
|
|
933
|
+
const padded = pad ? base64 + "=".repeat(4 - pad) : base64;
|
|
934
|
+
const decoded = JSON.parse(
|
|
935
|
+
Buffer.from(padded, "base64").toString("utf-8")
|
|
936
|
+
);
|
|
937
|
+
if (decoded && typeof decoded.transStatus === "string") {
|
|
938
|
+
return decoded.transStatus;
|
|
939
|
+
}
|
|
940
|
+
} catch (e) {
|
|
941
|
+
logger.warn(
|
|
942
|
+
"[3ds-callback] Could not decode CRES payload:",
|
|
943
|
+
e instanceof Error ? e.message : e
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
function paramsToObject(params) {
|
|
949
|
+
const obj = {};
|
|
950
|
+
for (const [k, v] of params.entries()) obj[k] = v;
|
|
951
|
+
return obj;
|
|
952
|
+
}
|
|
919
953
|
function buildCompletionPage(orderId, transStatus) {
|
|
954
|
+
const safeOrderId = String(orderId).replace(/[^a-zA-Z0-9_-]/g, "");
|
|
955
|
+
const safeTransStatus = String(transStatus).replace(/[^a-zA-Z0-9]/g, "");
|
|
920
956
|
const html = `<!DOCTYPE html>
|
|
921
957
|
<html><head><meta charset="utf-8"><title>3DS Verification</title></head>
|
|
922
958
|
<body>
|
|
@@ -924,8 +960,8 @@ function buildCompletionPage(orderId, transStatus) {
|
|
|
924
960
|
(function() {
|
|
925
961
|
var message = {
|
|
926
962
|
type: "3DS_COMPLETE",
|
|
927
|
-
orderId: "${
|
|
928
|
-
transStatus: "${
|
|
963
|
+
orderId: "${safeOrderId}",
|
|
964
|
+
transStatus: "${safeTransStatus}"
|
|
929
965
|
};
|
|
930
966
|
try {
|
|
931
967
|
if (window.parent && window.parent !== window) {
|
|
@@ -948,46 +984,68 @@ Verificando autenticación...
|
|
|
948
984
|
function create3dsCallbackHandler(deps) {
|
|
949
985
|
const logger = deps.logger ?? console;
|
|
950
986
|
const { db } = deps.firebase;
|
|
951
|
-
async function
|
|
952
|
-
if (orderId
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
987
|
+
async function persist(orderId, cres, transStatus) {
|
|
988
|
+
if (!orderId) return;
|
|
989
|
+
try {
|
|
990
|
+
const ref = db.collection("orders").doc(orderId);
|
|
991
|
+
const snap = await ref.get();
|
|
992
|
+
const status = snap.data()?.status;
|
|
993
|
+
if (!snap.exists || status !== "3ds-pending") {
|
|
994
|
+
logger.warn(
|
|
995
|
+
"[3ds-callback] rejected: order not in 3ds-pending state",
|
|
996
|
+
{ orderId, status }
|
|
997
|
+
);
|
|
998
|
+
return;
|
|
960
999
|
}
|
|
1000
|
+
const update = { updatedAt: /* @__PURE__ */ new Date() };
|
|
1001
|
+
if (cres) update.threeDSCres = cres;
|
|
1002
|
+
if (transStatus) update.threeDSTransStatus = transStatus;
|
|
1003
|
+
await ref.update(update);
|
|
1004
|
+
} catch (err) {
|
|
1005
|
+
logger.error("[3ds-callback] failed to store cres on order:", err);
|
|
961
1006
|
}
|
|
962
1007
|
}
|
|
1008
|
+
function resolveTransStatus(cres, fallback) {
|
|
1009
|
+
if (cres) {
|
|
1010
|
+
const fromCres = transStatusFromCres(cres, logger);
|
|
1011
|
+
if (fromCres) return fromCres;
|
|
1012
|
+
}
|
|
1013
|
+
return fallback;
|
|
1014
|
+
}
|
|
963
1015
|
const POST = async function POST2(request) {
|
|
964
|
-
const
|
|
965
|
-
const orderId = searchParams.get("orderId") || "";
|
|
1016
|
+
const orderId = new URL(request.url).searchParams.get("orderId") || "";
|
|
966
1017
|
let transStatus = "U";
|
|
967
1018
|
let cres = "";
|
|
968
1019
|
try {
|
|
969
1020
|
const contentType = request.headers.get("content-type") || "";
|
|
1021
|
+
const raw = await request.text();
|
|
970
1022
|
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
cres = params.get("cres") || params.get("CRes") || "";
|
|
1023
|
+
const obj = paramsToObject(new URLSearchParams(raw));
|
|
1024
|
+
transStatus = obj.transStatus || obj.TransStatus || "U";
|
|
1025
|
+
cres = pickCres(obj);
|
|
975
1026
|
} else {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1027
|
+
try {
|
|
1028
|
+
const body = JSON.parse(raw);
|
|
1029
|
+
transStatus = body.transStatus || body.TransStatus || "U";
|
|
1030
|
+
cres = pickCres(body);
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (!cres && raw) {
|
|
1035
|
+
cres = pickCres(paramsToObject(new URLSearchParams(raw)));
|
|
979
1036
|
}
|
|
980
1037
|
} catch {
|
|
981
1038
|
}
|
|
982
|
-
|
|
1039
|
+
transStatus = resolveTransStatus(cres, transStatus);
|
|
1040
|
+
await persist(orderId, cres, transStatus);
|
|
983
1041
|
return buildCompletionPage(orderId, transStatus);
|
|
984
1042
|
};
|
|
985
1043
|
const GET = async function GET2(request) {
|
|
986
|
-
const
|
|
987
|
-
const orderId =
|
|
988
|
-
const
|
|
989
|
-
const
|
|
990
|
-
await
|
|
1044
|
+
const params = new URL(request.url).searchParams;
|
|
1045
|
+
const orderId = params.get("orderId") || "";
|
|
1046
|
+
const cres = params.get("cres") || params.get("CRes") || params.get("value") || "";
|
|
1047
|
+
const transStatus = resolveTransStatus(cres, params.get("transStatus") || "Y");
|
|
1048
|
+
await persist(orderId, cres, transStatus);
|
|
991
1049
|
return buildCompletionPage(orderId, transStatus);
|
|
992
1050
|
};
|
|
993
1051
|
return { POST, GET };
|