@pandait.tech/payment-nuvei 0.2.0 → 0.3.0

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 a Next.js App Router route module for the 3DS callback endpoint.
203
- * Returns `{ POST, GET }` because the ACS may call either depending on bank/issuer.
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
- * export const { POST, GET } = create3dsCallbackHandler({ firebase: { db } });
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>;
@@ -199,11 +199,12 @@ interface ThreeDSCallbackHandlerDeps {
199
199
  logger?: Pick<Console, "log" | "error" | "warn">;
200
200
  }
201
201
  /**
202
- * Creates a Next.js App Router route module for the 3DS callback endpoint.
203
- * Returns `{ POST, GET }` because the ACS may call either depending on bank/issuer.
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
- * export const { POST, GET } = create3dsCallbackHandler({ firebase: { db } });
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>;
@@ -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: "${orderId}",
928
- transStatus: "${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&oacute;n...
948
984
  function create3dsCallbackHandler(deps) {
949
985
  const logger = deps.logger ?? console;
950
986
  const { db } = deps.firebase;
951
- async function storeCres(orderId, cres) {
952
- if (orderId && cres) {
953
- try {
954
- await db.collection("orders").doc(orderId).update({
955
- threeDSCres: cres,
956
- updatedAt: /* @__PURE__ */ new Date()
957
- });
958
- } catch (err) {
959
- logger.error("Failed to store cres on order:", err);
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 { searchParams } = new URL(request.url);
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 text = await request.text();
972
- const params = new URLSearchParams(text);
973
- transStatus = params.get("transStatus") || "U";
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
- const body = await request.json();
977
- transStatus = body.transStatus || "U";
978
- cres = body.cres || body.CRes || "";
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
- await storeCres(orderId, cres);
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 { searchParams } = new URL(request.url);
987
- const orderId = searchParams.get("orderId") || "";
988
- const transStatus = searchParams.get("transStatus") || "Y";
989
- const cres = searchParams.get("cres") || searchParams.get("CRes") || "";
990
- await storeCres(orderId, cres);
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 };