@authme/identity-verification 2.8.38 → 2.8.42

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.
package/index.js CHANGED
@@ -7,8 +7,8 @@ import 'core-js/modules/es.promise.js';
7
7
  import { getTranslateInstance, EventListenerService, TrackingEvent, generateStatus, StatusDescription, AuthmeError, ErrorCode, Feature, StatusEvent, StatusView, StatusAction, setRequestLoggingFunc, setAccessToken, getCustomerState } from '@authme/core';
8
8
  import { EAuthMeFASServiceStatus, EAuthMeIDCardAntiFraudStage as EAuthMeIDCardAntiFraudStage$1, EAuthMeCardClass as EAuthMeCardClass$1, AuthmeFunctionModule, MlEngine, EngineModule, EAuthMeEngineReturnCode, AuthmeEngineModuleBase } from '@authme/engine';
9
9
  import { IdRecognitionCardType, CountryCode, EAuthMeCardClass, EAuthMeIDCardAntiFraudStatus, EAuthMeIDCardAntiFraudStage, thicknessDefaultConfig, mapCardtypeToAuthmeClass, getRecognitionColumnOrder, cardTypeTitle, cardTypeConfirmTitle, cardTypeHeader, EAuthMeCardOCRStatus, EAuthMeMRZServiceStatus, saveExtraDoc, initScanDocumentResourceBase64, MRZService, option, themeUI, initScan, initScanDocument, ResourceImageType, uploadFrameBase64, finishScanDocument, CardOCR, IdCardAntiFraudService, twoWayAuthmeCardClassMap, RecognitionFileType, recognizeBase64, getCardSubTypes, getCardTypes, confirmScan } from '@authme/id-recognition';
10
- import { getCssVariable, RGBToLottieColor, colorToRGB, Storage, useState, uiThemeText, uiThemeHint, clearCanvas, getImageData, isMobile, hidePopup, showPopup, waitTime, TIME_UNIT, AuthmeError as AuthmeError$1, ErrorCode as ErrorCode$1, uiThemeButton, uiThemeSmallButton, requestCamera, showElement, asyncOnLineShowErrorMessage, getCanvasSize, startSpinner, stopSpinner, themeConfigDefault, uiThemeDirection, fontWeight, hideElement, dataURItoBlob, showErrorMessage, checkOnlineStatus, dropMenu, hideErrorMessage, UintArrayToBlob, isIphone14proOrProMax, cropByRatio, switchCamera, asyncShowPopup, retryPromiseWithCondition, asyncShowErrorMessage, uploadModal, backgroundRequest, debugTools, RUN_FUNCTION_NAME, STORAGE_KEY, splitResult, DEVICE_TYPE, combineResult, startLoadingSDK, stopLoadingSDK } from '@authme/util';
11
- import { mergeMap, animationFrames, filter, tap, map, from, catchError, EMPTY, of, merge, fromEvent, concatAll, takeUntil, Subject, defer, throttleTime, switchMap, raceWith, concatMap, throwError, finalize, timer, Observable, take, interval, mapTo, firstValueFrom, shareReplay, switchMapTo, takeWhile as takeWhile$1, race } from 'rxjs';
10
+ import { getCssVariable, RGBToLottieColor, colorToRGB, Storage, useState, uiThemeText, uiThemeHint, clearCanvas, getImageData, isMobile, hidePopup, showPopup, waitTime, TIME_UNIT, AuthmeError as AuthmeError$1, ErrorCode as ErrorCode$1, uiThemeButton, requestCamera, showElement, asyncOnLineShowErrorMessage, getCanvasSize, startSpinner, stopSpinner, themeConfigDefault, uiThemeDirection, fontWeight, hideElement, dataURItoBlob, showErrorMessage, checkOnlineStatus, uiThemeSmallButton, dropMenu, hideErrorMessage, UintArrayToBlob, isIphone14proOrProMax, cropByRatio, switchCamera, asyncShowPopup, retryPromiseWithCondition, asyncShowErrorMessage, uploadModal, backgroundRequest, debugTools, RUN_FUNCTION_NAME, STORAGE_KEY, splitResult, DEVICE_TYPE, combineResult, startLoadingSDK, stopLoadingSDK } from '@authme/util';
11
+ import { mergeMap, animationFrames, filter, tap, map, from, catchError, EMPTY, of, merge, fromEvent, concatAll, takeUntil, Subject, defer, throttleTime, switchMap, take, concatMap, throwError, finalize, Observable, interval, mapTo, firstValueFrom, shareReplay, switchMapTo, timer, takeWhile as takeWhile$1, race } from 'rxjs';
12
12
  import 'core-js/modules/es.regexp.to-string.js';
13
13
  import { FasRecognitionResult, EAuthMeFASServiceStage, FasService, LivenessAPI, EAuthMeMouthStatus, LivenessResultStatus } from '@authme/liveness';
14
14
  import 'core-js/modules/es.parse-float.js';
@@ -29079,6 +29079,7 @@ const fasRecognitionResultMapping = fasRecognitionResult => {
29079
29079
  [FasRecognitionResult.Pass]: StatusDescription.Pass,
29080
29080
  [FasRecognitionResult.Error]: StatusDescription.Error,
29081
29081
  [FasRecognitionResult.NeedMoreFrame]: StatusDescription.NeedMoreFrame,
29082
+ [FasRecognitionResult.Motion]: StatusDescription.Motion,
29082
29083
  [FasRecognitionResult.NeedFaceToCamera]: StatusDescription.NeedFaceToCamera
29083
29084
  };
29084
29085
  return (_a = fasServiceStageMap[fasRecognitionResult]) !== null && _a !== void 0 ? _a : StatusDescription.Failed;
@@ -29192,7 +29193,12 @@ const sendFrame = (canvasSizeInfo, canvas, video, frameCallback, fps, bas64Forma
29192
29193
  const ctx = canvas.getContext('2d', {
29193
29194
  willReadFrequently: true
29194
29195
  });
29195
- return source$.pipe(mergeMap(() => animationFrames().pipe(limitFPS(fps), filter(() => received && !(flags === null || flags === void 0 ? void 0 : flags.animating)), tap(() => received = false), tap(() => clearCanvas(canvas)), map(() => getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), mergeMap(imageData => from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(catchError(e => {
29196
+ return source$.pipe(mergeMap(() => animationFrames().pipe(limitFPS(fps), filter(() => received && !(flags === null || flags === void 0 ? void 0 : flags.animating)), tap(() => received = false), tap(() => clearCanvas(canvas)), map(() => getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), tap(imageData => {
29197
+ // 如果 imageData 為 null(canvas 尺寸無效),重置 received 讓下一幀可以繼續
29198
+ if (imageData === null) {
29199
+ received = true;
29200
+ }
29201
+ }), filter(imageData => imageData !== null), mergeMap(imageData => from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(catchError(e => {
29196
29202
  // send to fast, ignore
29197
29203
  if (e instanceof AuthmeError && e.code === ErrorCode.RECOGNITION_NOT_AVAILABLE) {
29198
29204
  return EMPTY;
@@ -29354,78 +29360,6 @@ const modal = arg => {
29354
29360
  authmeContainer.appendChild(domModal);
29355
29361
  };
29356
29362
 
29357
- const popupView = arg => {
29358
- const authmeContainer = document.querySelector('.authme-container');
29359
- if (!authmeContainer) {
29360
- console.error('modal: authmeContainer not found');
29361
- return;
29362
- }
29363
- const uiThemeConfig = Storage.getItem('themeConfig');
29364
- function removePopview() {
29365
- var _a;
29366
- (_a = document.querySelector('.video-container__popupview')) === null || _a === void 0 ? void 0 : _a.remove();
29367
- }
29368
- const domPopupView = document.createElement('div');
29369
- const domPopupViewContainer = document.createElement('div');
29370
- const domTitleContainer = document.createElement('div');
29371
- const domTitle = document.createElement('div');
29372
- const domContentContainer = document.createElement('div');
29373
- const domContent = document.createElement('div');
29374
- const domFooterContainer = document.createElement('div');
29375
- const domConfirm = document.createElement('div');
29376
- const domCancel = document.createElement('div');
29377
- domPopupView.classList.add('video-container__popupview');
29378
- domPopupViewContainer.classList.add('video-container__popupview-container');
29379
- domTitleContainer.classList.add('video-container__popupview-title-container');
29380
- domTitle.classList.add('video-container__popupview-title');
29381
- domContentContainer.classList.add('video-container__popupview-content-container');
29382
- domContent.classList.add('video-container__popupview-content');
29383
- domFooterContainer.classList.add('video-container__popupview-footer-container');
29384
- domConfirm.classList.add('video-container__popupview-confirm');
29385
- domCancel.classList.add('video-container__popupview-cancel');
29386
- domPopupViewContainer.style.backgroundColor = uiThemeConfig.popupView.backgroundColor;
29387
- domPopupViewContainer.style.borderRadius = `${uiThemeConfig.popupView.cornerRadius}px`;
29388
- domPopupViewContainer.style.width = `${parseFloat(uiThemeConfig.popupView.width) * 100}%`;
29389
- uiThemeText(domTitle, uiThemeConfig.titleOne);
29390
- uiThemeText(domContent, uiThemeConfig.bodyTwo);
29391
- console.log('deviceType', uiThemeConfig.deviceType);
29392
- if (uiThemeConfig.deviceType === 'mobile') {
29393
- uiThemeSmallButton(domConfirm, uiThemeConfig.smallermMajorButton);
29394
- } else {
29395
- uiThemeButton(domConfirm, uiThemeConfig.majorButton);
29396
- }
29397
- if (arg.cancel) {
29398
- uiThemeSmallButton(domCancel, uiThemeConfig.smallerMinorButton);
29399
- domCancel.innerHTML = arg.cancel;
29400
- domCancel.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
29401
- if (arg.onCancel) {
29402
- arg.onCancel();
29403
- }
29404
- removePopview();
29405
- }));
29406
- }
29407
- domConfirm.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
29408
- if (arg.onConfirm) {
29409
- arg.onConfirm();
29410
- }
29411
- removePopview();
29412
- }));
29413
- domTitle.innerHTML = arg.title;
29414
- domContent.innerHTML = arg.content;
29415
- domConfirm.innerHTML = arg.confirm;
29416
- domPopupViewContainer.appendChild(domTitleContainer);
29417
- domTitleContainer.appendChild(domTitle);
29418
- domPopupViewContainer.appendChild(domContentContainer);
29419
- domContentContainer.appendChild(domContent);
29420
- domPopupViewContainer.appendChild(domFooterContainer);
29421
- if (arg.cancel) {
29422
- domFooterContainer.appendChild(domCancel);
29423
- }
29424
- domFooterContainer.appendChild(domConfirm);
29425
- domPopupView.appendChild(domPopupViewContainer);
29426
- authmeContainer.appendChild(domPopupView);
29427
- };
29428
-
29429
29363
  function startLiveness(config) {
29430
29364
  return __awaiter(this, void 0, void 0, function* () {
29431
29365
  const translateService = getTranslateInstance();
@@ -29449,7 +29383,7 @@ function startLiveness(config) {
29449
29383
  setStatusEvent$2(StatusEvent.Passive);
29450
29384
  setStatusView$1(StatusView.Init);
29451
29385
  let engineInited = false;
29452
- let expiredIn;
29386
+ let lastFasStatus = null;
29453
29387
  // render view
29454
29388
  // const {
29455
29389
  // container,
@@ -29487,44 +29421,6 @@ function startLiveness(config) {
29487
29421
  };
29488
29422
  const canvas = document.createElement('canvas');
29489
29423
  let uiThemeConfig = themeConfigDefault;
29490
- // let sdkFlowTimeout: NodeJS.Timeout;
29491
- let sdkFlowTimeout = null;
29492
- const timeout$ = new Subject();
29493
- // function makeSDKFlowTimeout(expiredIn: number) {
29494
- // return setTimeout(async () => {
29495
- // asyncShowErrorMessage(
29496
- // translateService.translate('sdk.general.error.timeout.content'),
29497
- // false
29498
- // );
29499
- // await waitTime(3 * TIME_UNIT.SECOND);
29500
- // timeout$.next({
29501
- // isSuccess: false as const,
29502
- // code: `${ErrorCode.ID_RECOGNITION_TIMEOUT}`,
29503
- // message: new AuthmeError(ErrorCode.ID_RECOGNITION_TIMEOUT).message,
29504
- // });
29505
- // }, expiredIn * TIME_UNIT.SECOND);
29506
- // }
29507
- function makeSDKFlowTimeout(expiredIn) {
29508
- return timer(expiredIn * TIME_UNIT.SECOND).pipe(switchMap(() => new Observable(observer => {
29509
- config.onDestroy();
29510
- popupView({
29511
- title: translateService.translate('sdk.general.error.timeout.title'),
29512
- content: translateService.translate('sdk.general.error.timeout.content'),
29513
- confirm: translateService.translate('sdk.general.confirm'),
29514
- onConfirm: () => {
29515
- observer.next(true);
29516
- observer.complete();
29517
- }
29518
- });
29519
- }))).subscribe(() => {
29520
- timeout$.next({
29521
- isSuccess: false,
29522
- code: `${ErrorCode.ID_RECOGNITION_TIMEOUT}`,
29523
- message: new AuthmeError(ErrorCode.ID_RECOGNITION_TIMEOUT).message,
29524
- data: {}
29525
- });
29526
- });
29527
- }
29528
29424
  function initOptions() {
29529
29425
  return __awaiter(this, void 0, void 0, function* () {
29530
29426
  const themeConfigApi = yield config.getOptionConfig();
@@ -29560,7 +29456,6 @@ function startLiveness(config) {
29560
29456
  // 需要等到介紹頁完成後,才開始執行 config.init (engine init 流程)。
29561
29457
  const init$ = defer(() => config.init()).pipe(tap(res => {
29562
29458
  livenessConfig = res.parameters;
29563
- expiredIn = res.expiredIn;
29564
29459
  }), catchError(error => __awaiter(this, void 0, void 0, function* () {
29565
29460
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
29566
29461
  const EVENT_NAME_ERROR_CODE = 7;
@@ -29618,6 +29513,9 @@ function startLiveness(config) {
29618
29513
  })));
29619
29514
  const applyTextByResult = result => {
29620
29515
  // debugLog('fas-response', result);
29516
+ if (lastFasStatus !== result.eStatus) {
29517
+ lastFasStatus = result.eStatus;
29518
+ }
29621
29519
  sendStatusDescription$2(fasRecognitionResultMapping(result.eStatus));
29622
29520
  switch (result.eStatus) {
29623
29521
  case FasRecognitionResult.NoFace:
@@ -29675,6 +29573,10 @@ function startLiveness(config) {
29675
29573
  uiComponentLiveness.statusText.textContent = translateService.translate('sdk.general.verify.success');
29676
29574
  setBorderStatus('pass');
29677
29575
  break;
29576
+ case FasRecognitionResult.Motion:
29577
+ uiComponentLiveness.statusText.textContent = translateService.translate('sdk.liveness.detection.motion');
29578
+ setBorderStatus('pass');
29579
+ break;
29678
29580
  case FasRecognitionResult.Pass:
29679
29581
  if (result.eStage === EAuthMeFASServiceStage.Scale) {
29680
29582
  setBorderStatus(null);
@@ -29766,9 +29668,6 @@ function startLiveness(config) {
29766
29668
  }
29767
29669
  setCorrectViewHeight();
29768
29670
  }), switchMap(() => init$), switchMap(() => requestCamera$), tap(() => {
29769
- if (expiredIn) {
29770
- sdkFlowTimeout = makeSDKFlowTimeout(expiredIn);
29771
- }
29772
29671
  engineInited = true;
29773
29672
  }), switchMap(() => from(waitTime(1)).pipe(tap(() => uiComponentBasic.videoContainer.style.zIndex = '101'))), switchMap(() => from(getCanvasSize(uiComponentBasic.video))), switchMap(canvasSizeInfo => from(config.onStart(canvasSizeInfo)).pipe(tap(params => {
29774
29673
  const {
@@ -29818,26 +29717,16 @@ function startLiveness(config) {
29818
29717
  // ),
29819
29718
  filter(({
29820
29719
  result
29821
- }) => (result.eStatus === FasRecognitionResult.Pass || result.eStatus === FasRecognitionResult.Failed) && result.eStage === EAuthMeFASServiceStage.Done), map(data => {
29822
- // console.log('data', data);
29720
+ }) => (result.eStatus === FasRecognitionResult.Pass || result.eStatus === FasRecognitionResult.Failed || result.eStatus === FasRecognitionResult.DepthFake) && result.eStage === EAuthMeFASServiceStage.Done), take(1),
29721
+ // 只處理第一個成功結果,防止 engine 繼續運行導致重複上傳
29722
+ map(data => {
29723
+ // DepthFake 和 Failed 都視為失敗
29724
+ const isSuccess = data.result.eStatus === FasRecognitionResult.Pass;
29823
29725
  return Object.assign(Object.assign({}, data), {
29824
- isSuccess: true
29726
+ isSuccess
29825
29727
  });
29826
- }), raceWith(timeout$) // 確保 timeout 之後一定會有失敗結果
29827
- )))),
29828
- // switchMap((resp) =>
29829
- // window.navigator.onLine
29830
- // ? of(resp)
29831
- // : from(
29832
- // asyncOnLineShowErrorMessage(
29833
- // translateService.translate('sdk.general.error.alert.offline'),
29834
- // translateService.translate('sdk.general.error.retry'),
29835
- // false
29836
- // )
29837
- // ).pipe(map(() => resp))
29838
- // ),
29839
- tap(res => {
29840
- // console.log('res', res);
29728
+ }))))), tap(res => {
29729
+ // Engine timeout 會在 res.isSuccess 為 false 時發生
29841
29730
  if (res.isSuccess) {
29842
29731
  sendStatusDescription$2(StatusDescription.UploadingStart);
29843
29732
  startSpinner({
@@ -29846,12 +29735,10 @@ function startLiveness(config) {
29846
29735
  backgroundOpaque: true
29847
29736
  });
29848
29737
  } else {
29849
- console.warn('Timeout 發生,返回錯誤結果');
29738
+ // Engine 返回 timeout 或其他失敗狀態
29850
29739
  throw new AuthmeError(ErrorCode.ID_RECOGNITION_TIMEOUT);
29851
29740
  }
29852
- }), filter(res => res.isSuccess),
29853
- // **只執行成功的情況**
29854
- switchMap(() => defer(() => config.onSuccess())));
29741
+ }), filter(res => res.isSuccess), switchMap(() => defer(() => config.onSuccess())));
29855
29742
  return of({}).pipe(() => {
29856
29743
  eventListenerService$2.start();
29857
29744
  setStatusView$1(StatusView.Init);
@@ -29954,11 +29841,6 @@ function startLiveness(config) {
29954
29841
  } finally {
29955
29842
  uiComponentBasic.video.srcObject = null;
29956
29843
  }
29957
- // if (sdkFlowTimeout) clearTimeout(sdkFlowTimeout);
29958
- if (sdkFlowTimeout) {
29959
- sdkFlowTimeout.unsubscribe();
29960
- sdkFlowTimeout = null;
29961
- }
29962
29844
  eventListenerService$2.stop();
29963
29845
  uiComponentBasic.container.remove();
29964
29846
  unsubscribe$.next();
@@ -30483,18 +30365,20 @@ const renderOCRMask = params => {
30483
30365
  borderOpacity = params.strokeOpacity;
30484
30366
  }
30485
30367
  if (params.type === 'bordered') {
30486
- const rectX = newCardPoints[0].x * windowWidth;
30487
- const rectY = newCardPoints[0].y * windowHeight;
30488
- const rectWidth = newCardPoints[1].x * windowWidth - rectX;
30489
- const rectHeight = newCardPoints[3].y * windowHeight - rectY;
30490
- pathOfFrame.setAttribute('visibility', 'hidden');
30491
- rectOfFrame.setAttribute('visibility', 'visible');
30492
- rectOfFrame.setAttribute('x', rectX.toString());
30493
- rectOfFrame.setAttribute('y', rectY.toString());
30494
- rectOfFrame.setAttribute('width', rectWidth.toString());
30495
- rectOfFrame.setAttribute('height', rectHeight.toString());
30496
- rectOfFrame.setAttribute('stroke-opacity', borderOpacity.toString());
30497
- svg.appendChild(rectOfFrame);
30368
+ // 使用 path 畫傾斜的四邊形框線(支援防偽的傾斜效果)
30369
+ rectOfFrame.setAttribute('visibility', 'hidden');
30370
+ pathOfFrame.setAttribute('visibility', 'visible');
30371
+ // 計算四個角的座標
30372
+ const points = newCardPoints.map(p => ({
30373
+ x: p.x * windowWidth,
30374
+ y: p.y * windowHeight
30375
+ }));
30376
+ // 使用 path 畫閉合的四邊形
30377
+ const dBordered = `M ${points[0].x},${points[0].y} ` + `L ${points[1].x},${points[1].y} ` + `L ${points[2].x},${points[2].y} ` + `L ${points[3].x},${points[3].y} Z`;
30378
+ pathOfFrame.setAttribute('d', dBordered);
30379
+ pathOfFrame.setAttribute('stroke-opacity', borderOpacity.toString());
30380
+ pathOfFrame.setAttribute('stroke-width', '3');
30381
+ svg.appendChild(pathOfFrame);
30498
30382
  } else {
30499
30383
  rectOfFrame.setAttribute('visibility', 'hidden');
30500
30384
  pathOfFrame.setAttribute('visibility', 'visible');
@@ -30584,11 +30468,10 @@ const renderOCRMask = params => {
30584
30468
  path.setAttribute('d', d);
30585
30469
  // Set the same path for the border to match the mask exactly
30586
30470
  borderPath.setAttribute('d', d);
30471
+ // Anti-fraud 模式下:隱藏 pathOfFrame 和 rectOfFrame,只顯示 borderPath(傾斜的框)
30587
30472
  if (antiFraud) {
30588
- setBorderType({
30589
- type: borderType,
30590
- mirrored: _mirrored
30591
- });
30473
+ pathOfFrame.setAttribute('visibility', 'hidden');
30474
+ rectOfFrame.setAttribute('visibility', 'hidden');
30592
30475
  }
30593
30476
  };
30594
30477
  function setBorderSuccess(color, opacity) {
@@ -30611,12 +30494,13 @@ const renderOCRMask = params => {
30611
30494
  if (borderType === 'bordered') {
30612
30495
  rectOfFrame.setAttribute('stroke', color);
30613
30496
  rectOfFrame.setAttribute('stroke-opacity', opacity.toString());
30614
- borderPath.setAttribute('stroke', color);
30615
- borderPath.setAttribute('stroke-opacity', opacity.toString());
30616
30497
  } else {
30617
30498
  pathOfFrame.setAttribute('stroke', color);
30618
30499
  pathOfFrame.setAttribute('stroke-opacity', opacity.toString());
30619
30500
  }
30501
+ // 同時更新 borderPath (用於 anti-fraud 模式的傾斜框線)
30502
+ borderPath.setAttribute('stroke', color);
30503
+ borderPath.setAttribute('stroke-opacity', opacity.toString());
30620
30504
  }
30621
30505
  function frameImage(faceMode, zIndex, base64, color, opacity) {
30622
30506
  color = color !== null && color !== void 0 ? color : OcrFrame.imageColor;
@@ -31914,6 +31798,78 @@ const fraudScanIntroPage = arg => {
31914
31798
  authmeContainer.appendChild(domModal);
31915
31799
  };
31916
31800
 
31801
+ const popupView = arg => {
31802
+ const authmeContainer = document.querySelector('.authme-container');
31803
+ if (!authmeContainer) {
31804
+ console.error('modal: authmeContainer not found');
31805
+ return;
31806
+ }
31807
+ const uiThemeConfig = Storage.getItem('themeConfig');
31808
+ function removePopview() {
31809
+ var _a;
31810
+ (_a = document.querySelector('.video-container__popupview')) === null || _a === void 0 ? void 0 : _a.remove();
31811
+ }
31812
+ const domPopupView = document.createElement('div');
31813
+ const domPopupViewContainer = document.createElement('div');
31814
+ const domTitleContainer = document.createElement('div');
31815
+ const domTitle = document.createElement('div');
31816
+ const domContentContainer = document.createElement('div');
31817
+ const domContent = document.createElement('div');
31818
+ const domFooterContainer = document.createElement('div');
31819
+ const domConfirm = document.createElement('div');
31820
+ const domCancel = document.createElement('div');
31821
+ domPopupView.classList.add('video-container__popupview');
31822
+ domPopupViewContainer.classList.add('video-container__popupview-container');
31823
+ domTitleContainer.classList.add('video-container__popupview-title-container');
31824
+ domTitle.classList.add('video-container__popupview-title');
31825
+ domContentContainer.classList.add('video-container__popupview-content-container');
31826
+ domContent.classList.add('video-container__popupview-content');
31827
+ domFooterContainer.classList.add('video-container__popupview-footer-container');
31828
+ domConfirm.classList.add('video-container__popupview-confirm');
31829
+ domCancel.classList.add('video-container__popupview-cancel');
31830
+ domPopupViewContainer.style.backgroundColor = uiThemeConfig.popupView.backgroundColor;
31831
+ domPopupViewContainer.style.borderRadius = `${uiThemeConfig.popupView.cornerRadius}px`;
31832
+ domPopupViewContainer.style.width = `${parseFloat(uiThemeConfig.popupView.width) * 100}%`;
31833
+ uiThemeText(domTitle, uiThemeConfig.titleOne);
31834
+ uiThemeText(domContent, uiThemeConfig.bodyTwo);
31835
+ console.log('deviceType', uiThemeConfig.deviceType);
31836
+ if (uiThemeConfig.deviceType === 'mobile') {
31837
+ uiThemeSmallButton(domConfirm, uiThemeConfig.smallermMajorButton);
31838
+ } else {
31839
+ uiThemeButton(domConfirm, uiThemeConfig.majorButton);
31840
+ }
31841
+ if (arg.cancel) {
31842
+ uiThemeSmallButton(domCancel, uiThemeConfig.smallerMinorButton);
31843
+ domCancel.innerHTML = arg.cancel;
31844
+ domCancel.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
31845
+ if (arg.onCancel) {
31846
+ arg.onCancel();
31847
+ }
31848
+ removePopview();
31849
+ }));
31850
+ }
31851
+ domConfirm.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
31852
+ if (arg.onConfirm) {
31853
+ arg.onConfirm();
31854
+ }
31855
+ removePopview();
31856
+ }));
31857
+ domTitle.innerHTML = arg.title;
31858
+ domContent.innerHTML = arg.content;
31859
+ domConfirm.innerHTML = arg.confirm;
31860
+ domPopupViewContainer.appendChild(domTitleContainer);
31861
+ domTitleContainer.appendChild(domTitle);
31862
+ domPopupViewContainer.appendChild(domContentContainer);
31863
+ domContentContainer.appendChild(domContent);
31864
+ domPopupViewContainer.appendChild(domFooterContainer);
31865
+ if (arg.cancel) {
31866
+ domFooterContainer.appendChild(domCancel);
31867
+ }
31868
+ domFooterContainer.appendChild(domConfirm);
31869
+ domPopupView.appendChild(domPopupViewContainer);
31870
+ authmeContainer.appendChild(domPopupView);
31871
+ };
31872
+
31917
31873
  const ocrResultModal = arg => {
31918
31874
  let modifiedDetails = {};
31919
31875
  const authmeContainer = document.querySelector('.authme-container');
@@ -32134,6 +32090,10 @@ const ocrResultModal = arg => {
32134
32090
  }
32135
32091
 
32136
32092
  function parseRegex(input, forceUnicode = false) {
32093
+ // 處理 undefined 或 null 輸入
32094
+ if (!input) {
32095
+ return null;
32096
+ }
32137
32097
  try {
32138
32098
  let pattern = input;
32139
32099
  let flags = '';
@@ -32428,6 +32388,7 @@ function startOCR(config) {
32428
32388
  fraudRetryTimes: 1,
32429
32389
  fraudTimeout: 52,
32430
32390
  fraudMaxFps: 2,
32391
+ ocrMaxFps: 2,
32431
32392
  captureTimeout: config.ocrConfig.captureTimeout
32432
32393
  };
32433
32394
  let cardSizeInfo = {
@@ -32721,9 +32682,9 @@ function startOCR(config) {
32721
32682
  uiComponentOCRMask.setCardBorderColor('error');
32722
32683
  break;
32723
32684
  case EAuthMeIDCardAntiFraudStatus.Error:
32724
- uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.error.alert.serverError');
32725
- showErrorMessage(translateService.translate('sdk.general.error.alert.serverError'), false);
32685
+ uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.serverError');
32726
32686
  sendStatusDescription$1(StatusDescription.Error);
32687
+ uiComponentOCRMask.setCardBorderColor('error');
32727
32688
  break;
32728
32689
  case EAuthMeIDCardAntiFraudStatus.NeedDeformationFrontal:
32729
32690
  needDeformationCount++;
@@ -32817,16 +32778,32 @@ function startOCR(config) {
32817
32778
  return ocrSendFrameAnimation;
32818
32779
  }
32819
32780
  }), map(x => x.result), concatMap(x => __awaiter(this, void 0, void 0, function* () {
32781
+ // V9: 檢測 stage 變化,如果 stage 變化了,先顯示 pass 顏色
32782
+ // V9 Engine 不再返回 StagePass 狀態,而是直接切換到下一個 stage
32783
+ const stageChanged = x.eStage !== currentAntiFraudStage && currentAntiFraudStage !== undefined && x.eStage !== EAuthMeIDCardAntiFraudStage.Done;
32784
+ if (stageChanged) {
32785
+ // 顯示 pass 顏色表示上一個 stage 完成
32786
+ uiComponentOCRMask.setCardBorderColor('pass');
32787
+ uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.stagePass');
32788
+ previosAntiFraudAnimateTime = Date.now();
32789
+ // 等待一段時間讓用戶看到顏色變化
32790
+ yield new Promise(resolve => setTimeout(resolve, 1500));
32791
+ yield stopAnimate();
32792
+ needDeformationCount = 0;
32793
+ previosAntiFraudAnimateTime = 0;
32794
+ // 更新 currentAntiFraudStage 並設定新的 stage 框線
32795
+ currentAntiFraudStage = x.eStage;
32796
+ // 調用 cardRotateByStage 更新框線位置,但傳入 mandatoryRotate = true
32797
+ yield cardRotateByStage(x.eStage, true);
32798
+ // 跳過這一幀的 applyTextByResult,直接返回
32799
+ return x;
32800
+ }
32820
32801
  yield applyTextByResult(x);
32821
32802
  cardRotateByStage(x.eStage);
32822
32803
  if (isTutorialFinish) {
32823
32804
  // 文字更新移到 cardRotateAnimationProcess 中,確保與動畫同步
32824
32805
  x.eStage;
32825
32806
  }
32826
- if (x.eStage !== currentAntiFraudStage) {
32827
- needDeformationCount = 0;
32828
- previosAntiFraudAnimateTime = 0;
32829
- }
32830
32807
  if (!uiThemeConfig.isFraudAnimationLoadingPageEnabled) {
32831
32808
  // if (config.ocrConfig.disableTutorial) {
32832
32809
  stopSpinner();
@@ -32911,6 +32888,12 @@ function startOCR(config) {
32911
32888
  sendStatusDescription$1(StatusDescription.Reflective);
32912
32889
  uiComponentOCRMask.setCardBorderColor('error');
32913
32890
  break;
32891
+ case EAuthMeCardOCRStatus.Gray:
32892
+ // V9 新增: Gray 狀態表示灰階或顏色異常,顯示與 Blur 類似的提示
32893
+ uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
32894
+ sendStatusDescription$1(StatusDescription.Gray);
32895
+ uiComponentOCRMask.setCardBorderColor('error');
32896
+ break;
32914
32897
  case EAuthMeCardOCRStatus.Blur:
32915
32898
  uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
32916
32899
  sendStatusDescription$1(StatusDescription.Blur);
@@ -32938,6 +32921,19 @@ function startOCR(config) {
32938
32921
  sendStatusDescription$1(StatusDescription.Reflective);
32939
32922
  uiComponentOCRMask.setCardBorderColor('error');
32940
32923
  break;
32924
+ case EAuthMeMRZServiceStatus.Blur:
32925
+ // V9 新增: MRZ Blur 狀態
32926
+ uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
32927
+ sendStatusDescription$1(StatusDescription.Blur);
32928
+ uiComponentOCRMask.setCardBorderColor('error');
32929
+ blurCount++;
32930
+ break;
32931
+ case EAuthMeMRZServiceStatus.MRZNotFound:
32932
+ // V9 新增: MRZ區域未找到(卡片存在但無法識別MRZ區)
32933
+ uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.wrongCardType');
32934
+ sendStatusDescription$1(StatusDescription.WrongCardType);
32935
+ uiComponentOCRMask.setCardBorderColor('error');
32936
+ break;
32941
32937
  }
32942
32938
  if (blurCount === 2) {
32943
32939
  showCameraSwitchButton(deviceMetas);
@@ -33070,7 +33066,12 @@ function startOCR(config) {
33070
33066
  const ctx = canvas.getContext('2d', {
33071
33067
  willReadFrequently: true
33072
33068
  });
33073
- return source$.pipe(mergeMap(() => animationFrames().pipe(limitFPS(fps), filter(() => received && !ocrSendFrameAnimation), tap(() => received = false), tap(() => clearCanvas(canvas)), map(() => getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), mergeMap(imageData => from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(catchError(e => {
33069
+ return source$.pipe(mergeMap(() => animationFrames().pipe(limitFPS(fps), filter(() => received && !ocrSendFrameAnimation), tap(() => received = false), tap(() => clearCanvas(canvas)), map(() => getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), tap(imageData => {
33070
+ // 如果 imageData 為 null(canvas 尺寸無效),重置 received 讓下一幀可以繼續
33071
+ if (imageData === null) {
33072
+ received = true;
33073
+ }
33074
+ }), filter(imageData => imageData !== null), mergeMap(imageData => from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(catchError(e => {
33074
33075
  // send to fast, ignore
33075
33076
  if (e instanceof AuthmeError && e.code === ErrorCode.RECOGNITION_NOT_AVAILABLE) {
33076
33077
  return EMPTY;
@@ -33082,7 +33083,7 @@ function startOCR(config) {
33082
33083
  })), tap(() => received = true))))));
33083
33084
  };
33084
33085
  const autoCapture = canvasSizeInfo => {
33085
- return of(canvasSizeInfo).pipe(handleOcrSendFrame(canvasSizeInfo, uiComponentOCR.image, uiComponentBasic.video, config.recognition, 30, false, config.ocrConfig.resultImageFormat, cardType, type), tap(x => applyTextByResult(x.result)), filter(({
33086
+ return of(canvasSizeInfo).pipe(handleOcrSendFrame(canvasSizeInfo, uiComponentOCR.image, uiComponentBasic.video, config.recognition, ocrEngineConfig.ocrMaxFps, false, config.ocrConfig.resultImageFormat, cardType, type), tap(x => applyTextByResult(x.result)), filter(({
33086
33087
  result
33087
33088
  }) => result.eStatus === EAuthMeCardOCRStatus.Pass || result.eStatus === EAuthMeMRZServiceStatus.Success), take(1), tap(() => {
33088
33089
  if (countdownCaptureTimer && countdownCaptureTimer.end && !countdownCaptureTimer.end()) {
@@ -33100,38 +33101,47 @@ function startOCR(config) {
33100
33101
  hideElement(uiComponentOCR.scanAnimationContainer);
33101
33102
  }), map(() => resp))), switchMap(({
33102
33103
  result
33103
- }) => from(type === EAuthMeCardClass.Passport && config.ocrConfig.disablePassportConfirm || !config.ocrConfig.confirmPageEnabled ? of(false) : checkConfirmImage(result.imageData, result.iWidth, result.iHeight)).pipe(switchMap(needRetry => {
33104
- if (countdownCaptureTimer && countdownCaptureTimer.end) {
33105
- if (needRetry && !countdownCaptureTimer.end()) {
33106
- countdownCaptureTimer = countdownTimer(captureTimeoutTimer, () => {
33107
- showElement(captureBtn);
33108
- toastManualCapture = toast({
33109
- message: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_19494_3109)"><path d="M19.5 19.5H4.5C4.10218 19.5 3.72064 19.342 3.43934 19.0607C3.15804 18.7794 3 18.3978 3 18V7.5C3 7.10218 3.15804 6.72064 3.43934 6.43934C3.72064 6.15804 4.10218 6 4.5 6H7.5L9 3.75H15L16.5 6H19.5C19.8978 6 20.2794 6.15804 20.5607 6.43934C20.842 6.72064 21 7.10218 21 7.5V18C21 18.3978 20.842 18.7794 20.5607 19.0607C20.2794 19.342 19.8978 19.5 19.5 19.5Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 15.75C13.864 15.75 15.375 14.239 15.375 12.375C15.375 10.511 13.864 9 12 9C10.136 9 8.625 10.511 8.625 12.375C8.625 14.239 10.136 15.75 12 15.75Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></g><defs><clipPath id="clip0_19494_3109"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>' + translateService.translate('sdk.general.manualCapture'),
33110
- transition: true
33104
+ }) => {
33105
+ // V9: 檢查卡片圖像是否可用
33106
+ const cardResult = result;
33107
+ const hasValidImage = cardResult.imageData && cardResult.iWidth > 0 && cardResult.iHeight > 0;
33108
+ if (!hasValidImage) {
33109
+ console.warn('[OCR] Card image not available, skipping confirm page. iWidth:', cardResult.iWidth, 'iHeight:', cardResult.iHeight, 'imageData:', cardResult.imageData ? 'exists' : 'null');
33110
+ }
33111
+ const shouldSkipConfirm = type === EAuthMeCardClass.Passport && config.ocrConfig.disablePassportConfirm || !config.ocrConfig.confirmPageEnabled || !hasValidImage; // V9: 如果沒有卡片圖像,跳過確認頁
33112
+ return from(shouldSkipConfirm ? of(false) : checkConfirmImage(cardResult.imageData, cardResult.iWidth, cardResult.iHeight)).pipe(switchMap(needRetry => {
33113
+ if (countdownCaptureTimer && countdownCaptureTimer.end) {
33114
+ if (needRetry && !countdownCaptureTimer.end()) {
33115
+ countdownCaptureTimer = countdownTimer(captureTimeoutTimer, () => {
33116
+ showElement(captureBtn);
33117
+ toastManualCapture = toast({
33118
+ message: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_19494_3109)"><path d="M19.5 19.5H4.5C4.10218 19.5 3.72064 19.342 3.43934 19.0607C3.15804 18.7794 3 18.3978 3 18V7.5C3 7.10218 3.15804 6.72064 3.43934 6.43934C3.72064 6.15804 4.10218 6 4.5 6H7.5L9 3.75H15L16.5 6H19.5C19.8978 6 20.2794 6.15804 20.5607 6.43934C20.842 6.72064 21 7.10218 21 7.5V18C21 18.3978 20.842 18.7794 20.5607 19.0607C20.2794 19.342 19.8978 19.5 19.5 19.5Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 15.75C13.864 15.75 15.375 14.239 15.375 12.375C15.375 10.511 13.864 9 12 9C10.136 9 8.625 10.511 8.625 12.375C8.625 14.239 10.136 15.75 12 15.75Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></g><defs><clipPath id="clip0_19494_3109"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>' + translateService.translate('sdk.general.manualCapture'),
33119
+ transition: true
33120
+ });
33111
33121
  });
33112
- });
33113
- countdownCaptureTimer.init();
33114
- }
33115
- if (!needRetry) {
33116
- countdownCaptureTimer.clear();
33117
- hideElement(captureBtn);
33122
+ countdownCaptureTimer.init();
33123
+ }
33124
+ if (!needRetry) {
33125
+ countdownCaptureTimer.clear();
33126
+ hideElement(captureBtn);
33127
+ }
33118
33128
  }
33119
- }
33120
- startSpinner({
33121
- text: translateService.translate('sdk.general.uploading'),
33122
- statement: translateService.translate('sdk.general.footer'),
33123
- backgroundOpaque: true
33124
- });
33125
- return needRetry ? of(true) : from(config.confirmImage({
33126
- type,
33127
- cardType
33128
- })).pipe(tap(() => sendStatusAction$1(StatusAction.Uploading)), map(confirmResp => !confirmResp));
33129
- }), tap(() => {
33130
- // hideElement(uiComponentOCR.confirmImageContainer);
33131
- hideElement(uiComponentOCR.confirmContainer);
33132
- stopSpinner();
33133
- showVideoElement();
33134
- }), switchMap(needRetry => needRetry ? recognition(true) : of(true)))));
33129
+ startSpinner({
33130
+ text: translateService.translate('sdk.general.uploading'),
33131
+ statement: translateService.translate('sdk.general.footer'),
33132
+ backgroundOpaque: true
33133
+ });
33134
+ return needRetry ? of(true) : from(config.confirmImage({
33135
+ type,
33136
+ cardType
33137
+ })).pipe(tap(() => sendStatusAction$1(StatusAction.Uploading)), map(confirmResp => !confirmResp));
33138
+ }), tap(() => {
33139
+ // hideElement(uiComponentOCR.confirmImageContainer);
33140
+ hideElement(uiComponentOCR.confirmContainer);
33141
+ stopSpinner();
33142
+ showVideoElement();
33143
+ }), switchMap(needRetry => needRetry ? recognition(true) : of(true)));
33144
+ }));
33135
33145
  };
33136
33146
  const recognition = retry => {
33137
33147
  return init(retry).pipe(tap(() => {
@@ -33423,7 +33433,7 @@ function startOCR(config) {
33423
33433
  const cardType = currentType('get', null).cardType;
33424
33434
  const ctx = uiComponentOCR.image.getContext('2d');
33425
33435
  const imageData = getImageData(uiComponentOCR.image, ctx, uiComponentBasic.video, canvasSizeInfo, false, config.ocrConfig.resultImageFormat);
33426
- const imageBlob = UintArrayToBlob(canvasSizeInfo.width, canvasSizeInfo.height, imageData.data);
33436
+ const imageBlob = imageData ? UintArrayToBlob(canvasSizeInfo.width, canvasSizeInfo.height, imageData.data) : undefined;
33427
33437
  const cancelResultObj = {
33428
33438
  isSuccess: false,
33429
33439
  code: `${ErrorCode.USER_CANCEL}`,
@@ -33774,7 +33784,7 @@ function startOCR(config) {
33774
33784
  function cardRotateByStage(stage, mandatoryRotate = false, point) {
33775
33785
  return __awaiter(this, void 0, void 0, function* () {
33776
33786
  if ((stage === EAuthMeIDCardAntiFraudStage.Done || stage === currentAntiFraudStage) && mandatoryRotate === false) return;
33777
- const cardMatchROI = point ? point : config.getCardMatchROI ? yield config.getCardMatchROI() : null;
33787
+ const cardMatchROI = point ? point : config.getCardMatchROI ? yield config.getCardMatchROI(stage) : null;
33778
33788
  if (!cardMatchROI || cardMatchROI.length == 0) throw new AuthmeError(ErrorCode.SDK_INTERNAL_ERROR, 'getCardMatchROI is null');
33779
33789
  uiComponentOCRMask.setCardBorderColor('error');
33780
33790
  // 只有左右翻轉時需要正規化座標(確保左右邊平行)
@@ -34508,6 +34518,11 @@ class LivenessVerifyModule {
34508
34518
  }
34509
34519
  }
34510
34520
 
34521
+ /**
34522
+ * Debug flag for FAS (Face Authentication Service) encryption flow.
34523
+ * Set to true to enable detailed logging for debugging encryption issues.
34524
+ */
34525
+ const DEBUG_FAS = false;
34511
34526
  function handleUploadError$1(_error) {
34512
34527
  return new Promise(resolve => {
34513
34528
  uploadModal({
@@ -34545,12 +34560,18 @@ class LivenessModule {
34545
34560
  let id = '';
34546
34561
  let pubKey = '';
34547
34562
  let shouldEncrypt = false;
34563
+ // 保存 API 返回的參數,避免在 onStart 中被覆蓋
34564
+ let apiTimeoutSec;
34565
+ let apiFasThreshold;
34548
34566
  const encryptDataBase64 = data => __awaiter(this, void 0, void 0, function* () {
34549
34567
  const dataString = JSON.stringify(data);
34550
34568
  const encoder = new TextEncoder();
34551
34569
  const uint8Array = encoder.encode(dataString);
34552
- // TODO check encrypt function
34553
34570
  const resultEncrypt = yield this.fasService.encryptBlob(uint8Array, pubKey);
34571
+ if (!resultEncrypt) {
34572
+ console.error('[encryptDataBase64] encryptBlob returned null/empty');
34573
+ throw new AuthmeError(ErrorCode.SDK_INTERNAL_ERROR, 'encryptBlob failed');
34574
+ }
34554
34575
  return resultEncrypt;
34555
34576
  });
34556
34577
  const handleUpload = (id, frameList, resultList, meta, config, shouldEncrypt, encryptDataBase64) => __awaiter(this, void 0, void 0, function* () {
@@ -34572,7 +34593,6 @@ class LivenessModule {
34572
34593
  try {
34573
34594
  const result = yield firstValueFrom(yield startLiveness({
34574
34595
  getOptionConfig: () => __awaiter(this, void 0, void 0, function* () {
34575
- console.log('config.deviceType', config.deviceType);
34576
34596
  const res = yield LivenessAPI.IdentityVerification.option(config.deviceType);
34577
34597
  const themeId = res.themeId;
34578
34598
  if (!themeId) {
@@ -34587,6 +34607,10 @@ class LivenessModule {
34587
34607
  id = resp.id;
34588
34608
  pubKey = resp.parameters.pubKey;
34589
34609
  shouldEncrypt = (_a = resp.shouldEncrypt) !== null && _a !== void 0 ? _a : shouldEncrypt;
34610
+ if (DEBUG_FAS) ;
34611
+ // 根據 shouldEncrypt 決定是否設置 report key
34612
+ // shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
34613
+ // shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
34590
34614
  if (!shouldEncrypt) {
34591
34615
  yield this.fasService.setPublicKeyForJson(pubKey);
34592
34616
  } else {
@@ -34601,8 +34625,11 @@ class LivenessModule {
34601
34625
  // fRight: 0.7,
34602
34626
  // fBottom: 0.7,
34603
34627
  // };
34604
- params.timeoutSec = resp.parameters.fasTimeout || params.timeoutSec;
34605
- params.fFASTh = resp.parameters.fasThreshold || params.fFASTh;
34628
+ // 保存 API 參數,在 onStart 中使用
34629
+ apiTimeoutSec = resp.parameters.fasTimeout || params.timeoutSec;
34630
+ apiFasThreshold = resp.parameters.fasThreshold || params.fFASTh;
34631
+ params.timeoutSec = apiTimeoutSec;
34632
+ params.fFASTh = apiFasThreshold;
34606
34633
  yield this.fasService.setParams(params);
34607
34634
  yield this.fasService.setStage(resp.parameters.fasStages.map(x => `EAuthMeFASServiceStage_${x}`));
34608
34635
  // return resp.parameters;
@@ -34645,8 +34672,15 @@ class LivenessModule {
34645
34672
  params.faceROI.fRight = 1 - (1 - widthPercent) / 2;
34646
34673
  params.faceROI.fTop = (1 - heightPercent) / 2;
34647
34674
  params.faceROI.fBottom = 1 - (1 - heightPercent) / 2;
34648
- yield this.fasService.setParams(params);
34675
+ // 保留 API 設定的 timeout 和 threshold,避免被 getParams() 的預設值覆蓋
34676
+ if (apiTimeoutSec !== undefined) {
34677
+ params.timeoutSec = apiTimeoutSec;
34678
+ }
34679
+ if (apiFasThreshold !== undefined) {
34680
+ params.fFASTh = apiFasThreshold;
34681
+ }
34649
34682
  yield this.fasService.setFrameSize(frameWidth, frameHeight);
34683
+ yield this.fasService.setParams(params);
34650
34684
  yield this.fasService.startSession();
34651
34685
  return params;
34652
34686
  }),
@@ -34801,12 +34835,27 @@ class LivenessModule {
34801
34835
  data: meta
34802
34836
  };
34803
34837
  if (shouldEncrypt) {
34804
- postData.data = atob(meta);
34838
+ // shouldEncrypt = true 時,沒有設置 report key,
34839
+ // getReport() 返回的是 base64 編碼的未加密 JSON
34840
+ // 需要先 atob 解碼成純 JSON 字串(保持為字串,與 Android SDK 一致)
34841
+ // 然後用 encryptDataBase64 加密整個 postData
34842
+ try {
34843
+ const decodedMeta = atob(meta);
34844
+ if (DEBUG_FAS) ;
34845
+ postData.data = decodedMeta; // 保持為字串,與 Android 一致
34846
+ } catch (e) {
34847
+ console.error('[uploadMeta] atob failed:', e);
34848
+ // 如果 atob 失敗,可能已經是純 JSON 字串,保持原樣
34849
+ }
34850
+
34851
+ const encrypted = yield encryptDataBase64(postData);
34805
34852
  return LivenessAPI.IdentityVerification.uploadMeta({
34806
34853
  id: id,
34807
- encryptedBase64String: yield encryptDataBase64(postData)
34854
+ encryptedBase64String: encrypted
34808
34855
  });
34809
34856
  } else {
34857
+ // shouldEncrypt = false 時,已設置 report key,
34858
+ // getReport() 返回的是 engine 加密過的 base64 字符串
34810
34859
  return LivenessAPI.IdentityVerification.uploadMeta(postData);
34811
34860
  }
34812
34861
  // return LivenessAPI.IdentityVerification.uploadMeta({
@@ -35108,13 +35157,19 @@ class MRZModule {
35108
35157
  pubKey = resp.parameters.pubKey;
35109
35158
  scanId = resp.scanId;
35110
35159
  uploadFullFrame = (_c = (_b = resp.parameters.fraud) === null || _b === void 0 ? void 0 : _b.collectAllFrames) !== null && _c !== void 0 ? _c : config.uploadFullFrame;
35111
- if (resp.parameters.pubKey) {
35160
+ // 根據 shouldEncrypt 決定是否設置 report key
35161
+ // shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
35162
+ // shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
35163
+ if (!shouldEncrypt) {
35112
35164
  this.engine.setPublicKeyForJson(pubKey);
35165
+ } else {
35166
+ this.engine.setPublicKeyForJson('');
35113
35167
  }
35114
35168
  yield this.mrzService.init();
35115
35169
  yield waitTime(100);
35116
35170
  return Object.assign(Object.assign({}, resp.parameters), {
35117
- expiredIn: resp.expiredIn
35171
+ expiredIn: resp.expiredIn,
35172
+ ocrMaxFps: 2
35118
35173
  });
35119
35174
  }),
35120
35175
  ocrStart: (points, type, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode, cardType, retry = false) => __awaiter(this, void 0, void 0, function* () {
@@ -35179,29 +35234,44 @@ class MRZModule {
35179
35234
  // 1. 最後結果統一使用 getFinalResult 取得的結果。
35180
35235
  // 2. 由於 engine-lib 限制,getFinalResult 必須要在 stop 之後呼叫。
35181
35236
  const finalResult = yield this.mrzService.getFinalResult();
35182
- latestTField = JSON.parse(yield this.mrzService.toJson(finalResult));
35183
- if (latestTField['birthDateCheckDigit']) {
35184
- delete latestTField['birthDateCheckDigit'];
35185
- }
35186
- if (latestTField['documentNumberCheckDigit']) {
35187
- delete latestTField['documentNumberCheckDigit'];
35188
- }
35189
- if (latestTField['expiryDateCheckDigit']) {
35190
- delete latestTField['expiryDateCheckDigit'];
35191
- }
35192
- if (latestTField['optionaldataCheckDigit']) {
35193
- delete latestTField['optionaldataCheckDigit'];
35194
- }
35195
- if (latestTField['overallCheckDigit']) {
35196
- delete latestTField['overallCheckDigit'];
35197
- }
35198
- switch (latestTField.gender) {
35199
- case 'M':
35200
- latestTField.gender = 'male';
35201
- break;
35202
- case 'F':
35203
- latestTField.gender = 'female';
35204
- break;
35237
+ if (finalResult) {
35238
+ const rawField = JSON.parse(yield this.mrzService.toJson(finalResult));
35239
+ // V9 API 回傳 snake_case 欄位,轉換為 camelCase 以匹配翻譯 key
35240
+ const snakeToCamelMap = {
35241
+ birth_date: 'birthDate',
35242
+ expiry_date: 'expiryDate',
35243
+ document_number: 'documentNumber',
35244
+ document_type: 'documentType',
35245
+ given_name: 'givenName',
35246
+ personal_number: 'personalNumber',
35247
+ sex: 'gender',
35248
+ surname: 'surname',
35249
+ nationality: 'nationality',
35250
+ country: 'country'
35251
+ };
35252
+ // 需要刪除的欄位 (check digit)
35253
+ const fieldsToDelete = ['birthDateCheckDigit', 'documentNumberCheckDigit', 'expiryDateCheckDigit', 'optionaldataCheckDigit', 'overallCheckDigit', 'birth_date_check_digit', 'document_number_check_digit', 'expiry_date_check_digit', 'optional_data_check_digit', 'overall_check_digit'];
35254
+ // 轉換欄位名稱並過濾 check digit
35255
+ latestTField = {};
35256
+ for (const [key, value] of Object.entries(rawField)) {
35257
+ if (fieldsToDelete.includes(key)) {
35258
+ continue; // 跳過 check digit 欄位
35259
+ }
35260
+
35261
+ const newKey = snakeToCamelMap[key] || key;
35262
+ latestTField[newKey] = value;
35263
+ }
35264
+ // 性別轉換
35265
+ if (latestTField === null || latestTField === void 0 ? void 0 : latestTField.gender) {
35266
+ switch (latestTField.gender) {
35267
+ case 'M':
35268
+ latestTField.gender = 'male';
35269
+ break;
35270
+ case 'F':
35271
+ latestTField.gender = 'female';
35272
+ break;
35273
+ }
35274
+ }
35205
35275
  }
35206
35276
  } else if (uploadFullFrame) {
35207
35277
  const image = UintArrayToBlob(frameWidth, frameHeight, data, virtualCanvas);
@@ -35337,6 +35407,8 @@ class MRZModule {
35337
35407
  getAntiFraudStageList: () => []
35338
35408
  }));
35339
35409
  } catch (error) {
35410
+ console.error('[MRZ Module] Error caught:', error);
35411
+ console.error('[MRZ Module] Error stack:', error === null || error === void 0 ? void 0 : error.stack);
35340
35412
  const catchedError = error instanceof AuthmeError ? error : new AuthmeError(ErrorCode.SDK_INTERNAL_ERROR, error);
35341
35413
  return {
35342
35414
  isSuccess: false,
@@ -35349,8 +35421,98 @@ class MRZModule {
35349
35421
  }
35350
35422
  }
35351
35423
 
35352
- const F_IMAGE_BLUE_TH = 'fImageBlurTh';
35424
+ // V9: 卡片 OCR 參數常數名稱
35425
+ const F_IMAGE_BLUR_TH = 'fImageBlurTh';
35426
+ const F_IDCARD_COLOR_TH = 'fIdCardColorTh';
35427
+ const F_IMAGE_REFLECTIVE_TRIGGER_TH = 'fImageReflectiveTriggerTh';
35428
+ const F_IMAGE_REFLECTIVE_MASK_TH = 'fImageReflectiveMaskTh';
35429
+ const F_CARD_MATCH_TH = 'fCardMatchTh';
35430
+ const F_CARD_CLASSIFICATION_TH = 'fCardClassificationTh';
35431
+ // V9: 不同卡片類型的預設參數 (參考 Android SDK)
35432
+ // 邏輯說明:
35433
+ // - idCardColorTh: color_detect.score > threshold 為彩色 (Pass), < threshold 為灰階 (Gray)
35434
+ // - imageReflectiveTriggerTh: 反光分數 > threshold 判定為反光
35435
+ // - imageBlurTh: 模糊分數 > threshold 判定為模糊
35436
+ const CARD_OCR_PARAMS_MAP = {
35437
+ // 身分證正面: 高反光容限 (塑膠卡)
35438
+ TWN_IDCard_Front: {
35439
+ idCardColorTh: 15,
35440
+ imageBlurTh: 750,
35441
+ imageReflectiveTriggerTh: 1.05,
35442
+ imageReflectiveMaskTh: 1.5,
35443
+ cardMatchTh: 0.3,
35444
+ cardClassificationTh: 0.62655
35445
+ },
35446
+ // 身分證背面: 高反光容限 (塑膠卡)
35447
+ TWN_IDCard_Back: {
35448
+ idCardColorTh: 4.5,
35449
+ imageBlurTh: 750,
35450
+ imageReflectiveTriggerTh: 1.05,
35451
+ imageReflectiveMaskTh: 1.5,
35452
+ cardMatchTh: 0.3,
35453
+ cardClassificationTh: 0.62655
35454
+ },
35455
+ // 健保卡正面: 中等反光容限
35456
+ TWN_HealthCard_Front: {
35457
+ idCardColorTh: 10,
35458
+ imageBlurTh: 750,
35459
+ imageReflectiveTriggerTh: 0.35,
35460
+ imageReflectiveMaskTh: 0.45,
35461
+ cardMatchTh: 0.3,
35462
+ cardClassificationTh: 0.62655
35463
+ },
35464
+ // 駕照正面: 低反光觸發 (紙質)
35465
+ TWN_DriverLicense_Front: {
35466
+ idCardColorTh: 15,
35467
+ imageBlurTh: 750,
35468
+ imageReflectiveTriggerTh: 0.16,
35469
+ imageReflectiveMaskTh: 0.45,
35470
+ cardMatchTh: 0.3,
35471
+ cardClassificationTh: 0.62655
35472
+ },
35473
+ // 駕照背面: 極低反光觸發
35474
+ TWN_DriverLicense_Back: {
35475
+ idCardColorTh: 8,
35476
+ imageBlurTh: 600,
35477
+ imageReflectiveTriggerTh: 0.045,
35478
+ imageReflectiveMaskTh: 0.45,
35479
+ cardMatchTh: 0.75,
35480
+ cardClassificationTh: 0.62655
35481
+ },
35482
+ // 居留證正面
35483
+ TWN_ResidentCard_Front: {
35484
+ idCardColorTh: 0,
35485
+ imageBlurTh: 600,
35486
+ imageReflectiveTriggerTh: 0.045,
35487
+ imageReflectiveMaskTh: 0.45,
35488
+ cardMatchTh: 0.75,
35489
+ cardClassificationTh: 0.62655
35490
+ },
35491
+ // 居留證背面
35492
+ TWN_ResidentCard_Back: {
35493
+ idCardColorTh: 0,
35494
+ imageBlurTh: 600,
35495
+ imageReflectiveTriggerTh: 0.045,
35496
+ imageReflectiveMaskTh: 0.45,
35497
+ cardMatchTh: 0.75,
35498
+ cardClassificationTh: 0.62655
35499
+ }
35500
+ };
35501
+ // V9: 預設參數 (用於未定義的卡片類型)
35502
+ const DEFAULT_CARD_OCR_PARAMS = {
35503
+ idCardColorTh: 0,
35504
+ imageBlurTh: 750,
35505
+ imageReflectiveTriggerTh: 0.05,
35506
+ imageReflectiveMaskTh: 0.025,
35507
+ cardMatchTh: 0.3,
35508
+ cardClassificationTh: 0.62655
35509
+ };
35353
35510
  const DEFAULT_ANTI_FRAUD_TIMEOUT = 40;
35511
+ // V9: OCR 結果需要過濾掉的欄位 (重複/內部欄位)
35512
+ // 這些欄位可能是內部檢核數字或與其他欄位重複
35513
+ const OCR_FIELDS_TO_FILTER = ['backSideId', 'reverseId', 'dob', 'firstName', 'lastName', 'fullName', 'optionalData2' // 內部欄位
35514
+ ];
35515
+
35354
35516
  function unionMerge(a, b) {
35355
35517
  const entries = Object.entries(a).concat(Object.entries(b));
35356
35518
  // union merge
@@ -35415,8 +35577,8 @@ function handleUploadError(error) {
35415
35577
  });
35416
35578
  });
35417
35579
  }
35418
- // 目前不需要隨機,若未來需要隨機可更新。
35419
- const antiFraudStageListMap = [[EAuthMeIDCardAntiFraudStage$1.Left, EAuthMeIDCardAntiFraudStage$1.Right, EAuthMeIDCardAntiFraudStage$1.Up, EAuthMeIDCardAntiFraudStage$1.Down, EAuthMeIDCardAntiFraudStage$1.Left, EAuthMeIDCardAntiFraudStage$1.Right, EAuthMeIDCardAntiFraudStage$1.Up, EAuthMeIDCardAntiFraudStage$1.Down, EAuthMeIDCardAntiFraudStage$1.Left, EAuthMeIDCardAntiFraudStage$1.Right, EAuthMeIDCardAntiFraudStage$1.Up, EAuthMeIDCardAntiFraudStage$1.Down]
35580
+ // V9: Engine 會自動重複 stages,只需要傳入基本的 4 個方向
35581
+ const antiFraudStageListMap = [[EAuthMeIDCardAntiFraudStage$1.Left, EAuthMeIDCardAntiFraudStage$1.Right, EAuthMeIDCardAntiFraudStage$1.Up, EAuthMeIDCardAntiFraudStage$1.Down]
35420
35582
  // [
35421
35583
  // EAuthMeIDCardAntiFraudStage.Left,
35422
35584
  // EAuthMeIDCardAntiFraudStage.Right,
@@ -35734,12 +35896,17 @@ class OCRModule {
35734
35896
  pubKey = resp.parameters.pubKey;
35735
35897
  uploadFullFrame = (_c = (_b = resp.parameters.fraud) === null || _b === void 0 ? void 0 : _b.collectAllFrames) !== null && _c !== void 0 ? _c : config.uploadFullFrame;
35736
35898
  fraudTimeout = (_e = (_d = resp.parameters.fraud) === null || _d === void 0 ? void 0 : _d.totalTimeout) !== null && _e !== void 0 ? _e : DEFAULT_ANTI_FRAUD_TIMEOUT;
35899
+ // 根據 shouldEncrypt 決定是否設置 report key
35900
+ // shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
35901
+ // shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
35737
35902
  if (!shouldEncrypt) {
35738
35903
  this.engine.setPublicKeyForJson(pubKey);
35739
35904
  } else {
35740
35905
  this.engine.setPublicKeyForJson('');
35741
35906
  }
35742
35907
  if (config.type === IdRecognitionCardType.IDCard && config.needAntiFraud) {
35908
+ // 先設定 stage 再 init,避免使用預設的 Frontal stage
35909
+ yield this.antiFraudInstance.setStage(antiFraudStageList);
35743
35910
  yield this.antiFraudInstance.init();
35744
35911
  } else if (config.type === IdRecognitionCardType.ResidentCard) {
35745
35912
  // workaround: resident card need MRZ, refactor later.
@@ -35751,7 +35918,8 @@ class OCRModule {
35751
35918
  yield waitTime(100);
35752
35919
  return Object.assign(Object.assign({}, resp.parameters), {
35753
35920
  expiredIn: resp.expiredIn,
35754
- captureTimeout: config.captureTimeout
35921
+ captureTimeout: config.captureTimeout,
35922
+ ocrMaxFps: 2
35755
35923
  });
35756
35924
  }),
35757
35925
  ocrStart: (points, type, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode, cardType, retry = false) => __awaiter(this, void 0, void 0, function* () {
@@ -35762,9 +35930,14 @@ class OCRModule {
35762
35930
  _service instanceof CardOCR ? yield _service.setType(type) : null;
35763
35931
  const newParams = {};
35764
35932
  const oldParams = yield this.ocrService.getParams();
35765
- newParams[F_IMAGE_BLUE_TH] = 750;
35766
- newParams['fCardMatchTh'] = 0.5;
35767
- newParams['fImageReflectiveTriggerTh'] = 0.15;
35933
+ // V9: 根據卡片類型設置參數 (參考 Android SDK)
35934
+ const cardParams = CARD_OCR_PARAMS_MAP[cardType] || DEFAULT_CARD_OCR_PARAMS;
35935
+ newParams[F_IMAGE_BLUR_TH] = cardParams.imageBlurTh;
35936
+ newParams[F_IDCARD_COLOR_TH] = cardParams.idCardColorTh;
35937
+ newParams[F_IMAGE_REFLECTIVE_TRIGGER_TH] = cardParams.imageReflectiveTriggerTh;
35938
+ newParams[F_IMAGE_REFLECTIVE_MASK_TH] = cardParams.imageReflectiveMaskTh;
35939
+ newParams[F_CARD_MATCH_TH] = cardParams.cardMatchTh;
35940
+ newParams[F_CARD_CLASSIFICATION_TH] = cardParams.cardClassificationTh;
35768
35941
  yield this.ocrService.setParams(Object.assign(Object.assign({}, oldParams), newParams));
35769
35942
  if (cardTypes.map(mapCardtypeToAuthmeClass).filter(n => n == EAuthMeCardClass$1.Unknown).length > 0) {
35770
35943
  this.ocrService.setOption({
@@ -35928,9 +36101,12 @@ class OCRModule {
35928
36101
  const newParams = {};
35929
36102
  const oldParams = yield _service.getParams();
35930
36103
  if (config.antiFraudIMetalTagValidCountTh !== false) newParams['iMetalTagValidCountTh'] = config.antiFraudIMetalTagValidCountTh;
36104
+ // V9: 根據卡片類型設置參數 (參考 Android SDK)
36105
+ const cardParams = CARD_OCR_PARAMS_MAP[cardType] || DEFAULT_CARD_OCR_PARAMS;
35931
36106
  newParams['timeoutSec'] = 1000;
35932
- newParams['fCardMatchTh'] = 0.5;
35933
- newParams['fImageReflectiveTriggerTh'] = 0.15;
36107
+ newParams[F_IDCARD_COLOR_TH] = cardParams.idCardColorTh;
36108
+ newParams[F_IMAGE_REFLECTIVE_TRIGGER_TH] = cardParams.imageReflectiveTriggerTh;
36109
+ newParams[F_CARD_MATCH_TH] = cardParams.cardMatchTh;
35934
36110
  newParams['fImageThicknessTh'] = 0.4;
35935
36111
  newParams['fCardDeformationTh'] = 10;
35936
36112
  newParams['enableCardInROI'] = 1;
@@ -36270,8 +36446,16 @@ class OCRModule {
36270
36446
  });
36271
36447
  const ocrOriginImg = UintArrayToBlob(frameWidth, frameHeight, data, virtualCanvas, config.resultImageFormat);
36272
36448
  const eClass = cardType !== null && cardType !== void 0 ? cardType : '';
36273
- if (result.eStatus === EAuthMeCardOCRStatus.Pass && result.imageData && !!docInfos[eClass].docId) {
36274
- const resultOcrImg = UintArrayToBlob(result.iWidth, result.iHeight, result === null || result === void 0 ? void 0 : result.imageData, virtualCanvas, config.resultImageFormat);
36449
+ if (result.eStatus === EAuthMeCardOCRStatus.Pass && !!docInfos[eClass].docId) {
36450
+ // V9: 如果有卡片圖像就使用它,否則使用原始幀圖像
36451
+ let resultOcrImg;
36452
+ if (result.imageData && result.iWidth > 0 && result.iHeight > 0) {
36453
+ resultOcrImg = UintArrayToBlob(result.iWidth, result.iHeight, result.imageData, virtualCanvas, config.resultImageFormat);
36454
+ } else {
36455
+ // V9 沒有返回卡片圖像,使用原始幀圖像作為替代
36456
+ console.warn('[OCR] V9: no card image available, using original frame image');
36457
+ resultOcrImg = ocrOriginImg;
36458
+ }
36275
36459
  docInfos[eClass].ocrImg = resultOcrImg;
36276
36460
  docInfos[eClass].ocrOriginImg = ocrOriginImg;
36277
36461
  yield _service.stop();
@@ -36357,6 +36541,12 @@ class OCRModule {
36357
36541
  } else {
36358
36542
  ocrOriginImg = docInfos[option.cardType].ocrOriginImg;
36359
36543
  }
36544
+ // V9: 檢查 ocrOriginImg 是否有效
36545
+ if (!ocrOriginImg) {
36546
+ console.error('[OCR] confirmImage: ocrOriginImg is null or undefined, cannot proceed');
36547
+ console.error('[OCR] confirmImage: option.cardType =', option.cardType, 'docInfos[cardType] =', docInfos[option.cardType]);
36548
+ return false;
36549
+ }
36360
36550
  // const base64Image = await blobToBase64(ocrOriginImg);
36361
36551
  // console.log('confirmImage', base64Image);
36362
36552
  const requestImg = yield encryptImageBase64(ocrOriginImg);
@@ -36483,6 +36673,8 @@ class OCRModule {
36483
36673
  const item2 = res.fields.find(item => item.name === item1.name);
36484
36674
  return item2 ? Object.assign(Object.assign({}, item1), item2) : item1;
36485
36675
  }).concat(res.fields.filter(item2 => !ocrResultFilds.some(item1 => item1.name === item2.name)));
36676
+ // V9: 過濾掉重複/內部欄位
36677
+ ocrResultFilds = ocrResultFilds.filter(item => !OCR_FIELDS_TO_FILTER.includes(item.name));
36486
36678
  delete docInfos[option.cardType];
36487
36679
  return true;
36488
36680
  } catch (error) {
@@ -36502,6 +36694,13 @@ class OCRModule {
36502
36694
  } else {
36503
36695
  ocrOriginImg = docInfos[cardType].ocrOriginImg;
36504
36696
  }
36697
+ // V9: 檢查 ocrOriginImg 是否有效
36698
+ if (!ocrOriginImg) {
36699
+ console.warn('[OCR] ocrCancel: ocrOriginImg is null, skipping upload');
36700
+ yield this.ocrService.stop();
36701
+ docInfos[cardType].docId = '';
36702
+ return true;
36703
+ }
36505
36704
  const requestImg = yield encryptImageBase64(ocrOriginImg);
36506
36705
  yield this.ocrService.stop();
36507
36706
  const report = yield this.ocrService.getReport();
@@ -36548,7 +36747,9 @@ class OCRModule {
36548
36747
  }
36549
36748
  }),
36550
36749
  antiFraudStart: (points, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode) => __awaiter(this, void 0, void 0, function* () {
36551
- yield this.antiFraudInstance.init();
36750
+ // setStage() 如果服務已初始化會自動觸發 reinit
36751
+ // 不需要再呼叫 init()
36752
+ yield this.antiFraudInstance.setStage(antiFraudStageList);
36552
36753
  yield this.antiFraudInstance.setFrameSize(frameWidth, frameHeight);
36553
36754
  yield this.antiFraudInstance.setMaskPosition(points.map(([x, y]) => [Number(x.toFixed(2)), Number(y.toFixed(2))]));
36554
36755
  const newParams = {};
@@ -36556,7 +36757,6 @@ class OCRModule {
36556
36757
  if (config.antiFraudIMetalTagValidCountTh !== false) newParams['iMetalTagValidCountTh'] = config.antiFraudIMetalTagValidCountTh;
36557
36758
  newParams['timeoutSec'] = fraudTimeout;
36558
36759
  yield this.antiFraudInstance.setParams(Object.assign(Object.assign({}, oldParams), newParams));
36559
- yield this.antiFraudInstance.setStage(antiFraudStageList);
36560
36760
  yield this.antiFraudInstance.startSession();
36561
36761
  const twnidCardFront = EAuthMeCardClass$1.TWN_IDCard_Front;
36562
36762
  docInfos[twnidCardFront] = {
@@ -36817,8 +37017,10 @@ class OCRModule {
36817
37017
  yield (_u = this.ocrService) === null || _u === void 0 ? void 0 : _u.destroy();
36818
37018
  yield (_v = this.antiFraudInstance) === null || _v === void 0 ? void 0 : _v.destroy();
36819
37019
  }),
36820
- getCardMatchROI: () => __awaiter(this, void 0, void 0, function* () {
36821
- return yield this.antiFraudInstance.getCardMatchROI();
37020
+ getCardMatchROI: stage => __awaiter(this, void 0, void 0, function* () {
37021
+ return yield this.antiFraudInstance.getCardMatchROI(stage ? {
37022
+ stage
37023
+ } : undefined);
36822
37024
  })
36823
37025
  }), eventNameWrong$.pipe(tap(() => {
36824
37026
  throw new AuthmeError(ErrorCode.EVENT_NAME_WRONG);
@@ -37335,8 +37537,8 @@ class AuthmeIdentityVerification extends AuthmeFunctionModule {
37335
37537
  }
37336
37538
 
37337
37539
  var name = "authme/sdk";
37338
- var version$1 = "2.8.38";
37339
- var date = "2026-01-21T02:32:26+0000";
37540
+ var version$1 = "2.8.42";
37541
+ var date = "2026-02-03T09:36:05+0000";
37340
37542
  var packageInfo = {
37341
37543
  name: name,
37342
37544
  version: version$1,