(function attachJiapuTacAdapter(global) { "use strict"; var SUPPORTED_TYPES = ["SLIDER", "ROTATE", "CONCAT", "WORD_IMAGE_CLICK"]; function fail(message, code) { var error = new Error(message); error.code = code || "TAC_PROTOCOL_INVALID"; throw error; } function text(value, label) { if (typeof value !== "string" || !value.trim()) fail(label + "不能为空"); return value.trim(); } function positiveInteger(value, label) { var number = Number(value); if (!Number.isInteger(number) || number < 1) fail(label + "必须是正整数"); return number; } function assertContext(context) { if (!context || typeof context !== "object") fail("TAC 请求上下文无效"); return { tenantId: text(context.tenantId, "租户标识"), clientId: text(context.clientId, "客户端标识"), sceneCode: text(context.sceneCode, "验证场景"), subject: text(context.subject, "验证主体"), providerCode: text(context.providerCode, "验证服务商").toUpperCase(), captchaType: text(context.captchaType, "验证码类型").toUpperCase(), }; } function assertChallengePayload(type, payload) { if (!payload || typeof payload !== "object" || Array.isArray(payload)) { fail("行为验证挑战缺少渲染载荷"); } if (type === "CONCAT") { text(payload.backgroundImage, "拼图背景图片"); positiveInteger(payload.backgroundImageHeight, "拼图背景高度"); if (!payload.data || !Number.isFinite(Number(payload.data.randomY))) { fail("拼图挑战缺少 randomY"); } return; } text(payload.backgroundImage, "行为验证背景图片"); text(payload.templateImage, "行为验证模板图片"); } function normalizeChallengeResponse(response, expectedContext) { var context = assertContext(expectedContext); if (!response || typeof response !== "object" || Number(response.code) !== 200) { fail((response && response.msg) || "行为验证挑战请求失败", "TAC_CHALLENGE_FAILED"); } var data = response.data; if (!data || typeof data !== "object" || data.required !== true) { fail("行为验证挑战未处于强制验证状态", "TAC_CHALLENGE_NOT_REQUIRED"); } var providerCode = text(data.providerCode, "挑战服务商").toUpperCase(); var captchaType = text(data.captchaType, "挑战验证码类型").toUpperCase(); if (providerCode !== "TIANAI" || providerCode !== context.providerCode) { fail("行为验证挑战服务商不匹配", "TAC_PROVIDER_MISMATCH"); } if (SUPPORTED_TYPES.indexOf(captchaType) < 0 || captchaType !== context.captchaType) { fail("行为验证挑战类型不匹配", "TAC_TYPE_MISMATCH"); } var challengeId = text(data.challengeId, "行为验证挑战标识"); var expireSeconds = positiveInteger(data.expireSeconds, "行为验证挑战有效期"); assertChallengePayload(captchaType, data.payload); return { challenge: { required: true, providerCode: providerCode, captchaType: captchaType, challengeId: challengeId, expireSeconds: expireSeconds, }, sdkResponse: { code: 200, msg: typeof response.msg === "string" ? response.msg : "操作成功", data: Object.assign({}, data.payload, { type: captchaType, id: challengeId, }), }, }; } function assertTrack(track) { if (!track || typeof track !== "object" || Array.isArray(track)) fail("行为验证轨迹无效"); positiveInteger(track.bgImageWidth, "轨迹背景宽度"); positiveInteger(track.bgImageHeight, "轨迹背景高度"); var startTime = Number(track.startTime); var stopTime = Number(track.stopTime); if (!Number.isFinite(startTime) || !Number.isFinite(stopTime) || stopTime < startTime) { fail("行为验证轨迹时间无效"); } if (!Array.isArray(track.trackList) || track.trackList.length < 1) { fail("行为验证轨迹不能为空"); } track.trackList.forEach(function validatePoint(point) { if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y)) || !Number.isFinite(Number(point.t)) || typeof point.type !== "string" || !point.type) { fail("行为验证轨迹点无效"); } }); return track; } function buildVerifyBody(libraryRequest, expectedContext, challenge) { var context = assertContext(expectedContext); if (!challenge || typeof challenge !== "object") fail("行为验证挑战上下文缺失"); var challengeId = text(challenge.challengeId, "行为验证挑战标识"); if (!libraryRequest || libraryRequest.id !== challengeId) { fail("行为验证 challenge 标识与当前挑战不匹配", "TAC_CHALLENGE_MISMATCH"); } if (challenge.providerCode !== context.providerCode || challenge.captchaType !== context.captchaType) { fail("行为验证挑战上下文已变化", "TAC_CONTEXT_MISMATCH"); } return { tenantId: context.tenantId, clientId: context.clientId, sceneCode: context.sceneCode, subject: context.subject, challengeId: challengeId, providerCode: challenge.providerCode, captchaType: challenge.captchaType, // 受保护 OpenAPI 的唯一合同是 payload.track;SDK 回调 id 已提升为 challengeId, // 不得再次混入 payload,也不得退回“直接传 track”的兼容分支。 payload: { track: assertTrack(libraryRequest.data) }, }; } function normalizeVerifyResponse(response) { if (!response || typeof response !== "object" || Number(response.code) !== 200) { fail((response && response.msg) || "行为验证请求失败", "TAC_VERIFY_REQUEST_FAILED"); } var data = response.data; if (!data || data.passed !== true) { fail((data && data.message) || "行为验证未通过", "TAC_VERIFY_REJECTED"); } var validToken = text(data.validToken, "行为验证票据"); var expireSeconds = positiveInteger(data.expireSeconds, "行为验证票据有效期"); return { code: 200, data: { passed: true, validToken: validToken, expireSeconds: expireSeconds }, }; } function toSdkFailure(error) { return { code: 4001, msg: error && error.message ? error.message : "行为验证失败,请重试", }; } global.JiapuTacAdapter = Object.freeze({ normalizeChallengeResponse: normalizeChallengeResponse, buildVerifyBody: buildVerifyBody, normalizeVerifyResponse: normalizeVerifyResponse, toSdkFailure: toSdkFailure, }); })(typeof window !== "undefined" ? window : globalThis);