@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/assets/locale/en_US.json +13 -2
- package/assets/locale/ja_JP.json +13 -2
- package/assets/locale/zh_Hant_TW.json +14 -2
- package/assets/styles/style.css +48 -1
- package/index.cjs +452 -250
- package/index.js +454 -252
- package/package.json +6 -6
- package/src/lib/interface/config.interface.d.ts +1 -0
- package/src/lib/ui/liveness-flow.d.ts +1 -2
- package/src/lib/ui/ocr-flow.d.ts +1 -1
package/index.cjs
CHANGED
|
@@ -29087,6 +29087,7 @@ const fasRecognitionResultMapping = fasRecognitionResult => {
|
|
|
29087
29087
|
[liveness.FasRecognitionResult.Pass]: core.StatusDescription.Pass,
|
|
29088
29088
|
[liveness.FasRecognitionResult.Error]: core.StatusDescription.Error,
|
|
29089
29089
|
[liveness.FasRecognitionResult.NeedMoreFrame]: core.StatusDescription.NeedMoreFrame,
|
|
29090
|
+
[liveness.FasRecognitionResult.Motion]: core.StatusDescription.Motion,
|
|
29090
29091
|
[liveness.FasRecognitionResult.NeedFaceToCamera]: core.StatusDescription.NeedFaceToCamera
|
|
29091
29092
|
};
|
|
29092
29093
|
return (_a = fasServiceStageMap[fasRecognitionResult]) !== null && _a !== void 0 ? _a : core.StatusDescription.Failed;
|
|
@@ -29200,7 +29201,12 @@ const sendFrame = (canvasSizeInfo, canvas, video, frameCallback, fps, bas64Forma
|
|
|
29200
29201
|
const ctx = canvas.getContext('2d', {
|
|
29201
29202
|
willReadFrequently: true
|
|
29202
29203
|
});
|
|
29203
|
-
return source$.pipe(rxjs.mergeMap(() => rxjs.animationFrames().pipe(limitFPS(fps), rxjs.filter(() => received && !(flags === null || flags === void 0 ? void 0 : flags.animating)), rxjs.tap(() => received = false), rxjs.tap(() => util.clearCanvas(canvas)), rxjs.map(() => util.getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), rxjs.
|
|
29204
|
+
return source$.pipe(rxjs.mergeMap(() => rxjs.animationFrames().pipe(limitFPS(fps), rxjs.filter(() => received && !(flags === null || flags === void 0 ? void 0 : flags.animating)), rxjs.tap(() => received = false), rxjs.tap(() => util.clearCanvas(canvas)), rxjs.map(() => util.getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), rxjs.tap(imageData => {
|
|
29205
|
+
// 如果 imageData 為 null(canvas 尺寸無效),重置 received 讓下一幀可以繼續
|
|
29206
|
+
if (imageData === null) {
|
|
29207
|
+
received = true;
|
|
29208
|
+
}
|
|
29209
|
+
}), rxjs.filter(imageData => imageData !== null), rxjs.mergeMap(imageData => rxjs.from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(rxjs.catchError(e => {
|
|
29204
29210
|
// send to fast, ignore
|
|
29205
29211
|
if (e instanceof core.AuthmeError && e.code === core.ErrorCode.RECOGNITION_NOT_AVAILABLE) {
|
|
29206
29212
|
return rxjs.EMPTY;
|
|
@@ -29362,78 +29368,6 @@ const modal = arg => {
|
|
|
29362
29368
|
authmeContainer.appendChild(domModal);
|
|
29363
29369
|
};
|
|
29364
29370
|
|
|
29365
|
-
const popupView = arg => {
|
|
29366
|
-
const authmeContainer = document.querySelector('.authme-container');
|
|
29367
|
-
if (!authmeContainer) {
|
|
29368
|
-
console.error('modal: authmeContainer not found');
|
|
29369
|
-
return;
|
|
29370
|
-
}
|
|
29371
|
-
const uiThemeConfig = util.Storage.getItem('themeConfig');
|
|
29372
|
-
function removePopview() {
|
|
29373
|
-
var _a;
|
|
29374
|
-
(_a = document.querySelector('.video-container__popupview')) === null || _a === void 0 ? void 0 : _a.remove();
|
|
29375
|
-
}
|
|
29376
|
-
const domPopupView = document.createElement('div');
|
|
29377
|
-
const domPopupViewContainer = document.createElement('div');
|
|
29378
|
-
const domTitleContainer = document.createElement('div');
|
|
29379
|
-
const domTitle = document.createElement('div');
|
|
29380
|
-
const domContentContainer = document.createElement('div');
|
|
29381
|
-
const domContent = document.createElement('div');
|
|
29382
|
-
const domFooterContainer = document.createElement('div');
|
|
29383
|
-
const domConfirm = document.createElement('div');
|
|
29384
|
-
const domCancel = document.createElement('div');
|
|
29385
|
-
domPopupView.classList.add('video-container__popupview');
|
|
29386
|
-
domPopupViewContainer.classList.add('video-container__popupview-container');
|
|
29387
|
-
domTitleContainer.classList.add('video-container__popupview-title-container');
|
|
29388
|
-
domTitle.classList.add('video-container__popupview-title');
|
|
29389
|
-
domContentContainer.classList.add('video-container__popupview-content-container');
|
|
29390
|
-
domContent.classList.add('video-container__popupview-content');
|
|
29391
|
-
domFooterContainer.classList.add('video-container__popupview-footer-container');
|
|
29392
|
-
domConfirm.classList.add('video-container__popupview-confirm');
|
|
29393
|
-
domCancel.classList.add('video-container__popupview-cancel');
|
|
29394
|
-
domPopupViewContainer.style.backgroundColor = uiThemeConfig.popupView.backgroundColor;
|
|
29395
|
-
domPopupViewContainer.style.borderRadius = `${uiThemeConfig.popupView.cornerRadius}px`;
|
|
29396
|
-
domPopupViewContainer.style.width = `${parseFloat(uiThemeConfig.popupView.width) * 100}%`;
|
|
29397
|
-
util.uiThemeText(domTitle, uiThemeConfig.titleOne);
|
|
29398
|
-
util.uiThemeText(domContent, uiThemeConfig.bodyTwo);
|
|
29399
|
-
console.log('deviceType', uiThemeConfig.deviceType);
|
|
29400
|
-
if (uiThemeConfig.deviceType === 'mobile') {
|
|
29401
|
-
util.uiThemeSmallButton(domConfirm, uiThemeConfig.smallermMajorButton);
|
|
29402
|
-
} else {
|
|
29403
|
-
util.uiThemeButton(domConfirm, uiThemeConfig.majorButton);
|
|
29404
|
-
}
|
|
29405
|
-
if (arg.cancel) {
|
|
29406
|
-
util.uiThemeSmallButton(domCancel, uiThemeConfig.smallerMinorButton);
|
|
29407
|
-
domCancel.innerHTML = arg.cancel;
|
|
29408
|
-
domCancel.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29409
|
-
if (arg.onCancel) {
|
|
29410
|
-
arg.onCancel();
|
|
29411
|
-
}
|
|
29412
|
-
removePopview();
|
|
29413
|
-
}));
|
|
29414
|
-
}
|
|
29415
|
-
domConfirm.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29416
|
-
if (arg.onConfirm) {
|
|
29417
|
-
arg.onConfirm();
|
|
29418
|
-
}
|
|
29419
|
-
removePopview();
|
|
29420
|
-
}));
|
|
29421
|
-
domTitle.innerHTML = arg.title;
|
|
29422
|
-
domContent.innerHTML = arg.content;
|
|
29423
|
-
domConfirm.innerHTML = arg.confirm;
|
|
29424
|
-
domPopupViewContainer.appendChild(domTitleContainer);
|
|
29425
|
-
domTitleContainer.appendChild(domTitle);
|
|
29426
|
-
domPopupViewContainer.appendChild(domContentContainer);
|
|
29427
|
-
domContentContainer.appendChild(domContent);
|
|
29428
|
-
domPopupViewContainer.appendChild(domFooterContainer);
|
|
29429
|
-
if (arg.cancel) {
|
|
29430
|
-
domFooterContainer.appendChild(domCancel);
|
|
29431
|
-
}
|
|
29432
|
-
domFooterContainer.appendChild(domConfirm);
|
|
29433
|
-
domPopupView.appendChild(domPopupViewContainer);
|
|
29434
|
-
authmeContainer.appendChild(domPopupView);
|
|
29435
|
-
};
|
|
29436
|
-
|
|
29437
29371
|
function startLiveness(config) {
|
|
29438
29372
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29439
29373
|
const translateService = core.getTranslateInstance();
|
|
@@ -29457,7 +29391,7 @@ function startLiveness(config) {
|
|
|
29457
29391
|
setStatusEvent$2(core.StatusEvent.Passive);
|
|
29458
29392
|
setStatusView$1(core.StatusView.Init);
|
|
29459
29393
|
let engineInited = false;
|
|
29460
|
-
let
|
|
29394
|
+
let lastFasStatus = null;
|
|
29461
29395
|
// render view
|
|
29462
29396
|
// const {
|
|
29463
29397
|
// container,
|
|
@@ -29495,44 +29429,6 @@ function startLiveness(config) {
|
|
|
29495
29429
|
};
|
|
29496
29430
|
const canvas = document.createElement('canvas');
|
|
29497
29431
|
let uiThemeConfig = util.themeConfigDefault;
|
|
29498
|
-
// let sdkFlowTimeout: NodeJS.Timeout;
|
|
29499
|
-
let sdkFlowTimeout = null;
|
|
29500
|
-
const timeout$ = new rxjs.Subject();
|
|
29501
|
-
// function makeSDKFlowTimeout(expiredIn: number) {
|
|
29502
|
-
// return setTimeout(async () => {
|
|
29503
|
-
// asyncShowErrorMessage(
|
|
29504
|
-
// translateService.translate('sdk.general.error.timeout.content'),
|
|
29505
|
-
// false
|
|
29506
|
-
// );
|
|
29507
|
-
// await waitTime(3 * TIME_UNIT.SECOND);
|
|
29508
|
-
// timeout$.next({
|
|
29509
|
-
// isSuccess: false as const,
|
|
29510
|
-
// code: `${ErrorCode.ID_RECOGNITION_TIMEOUT}`,
|
|
29511
|
-
// message: new AuthmeError(ErrorCode.ID_RECOGNITION_TIMEOUT).message,
|
|
29512
|
-
// });
|
|
29513
|
-
// }, expiredIn * TIME_UNIT.SECOND);
|
|
29514
|
-
// }
|
|
29515
|
-
function makeSDKFlowTimeout(expiredIn) {
|
|
29516
|
-
return rxjs.timer(expiredIn * util.TIME_UNIT.SECOND).pipe(rxjs.switchMap(() => new rxjs.Observable(observer => {
|
|
29517
|
-
config.onDestroy();
|
|
29518
|
-
popupView({
|
|
29519
|
-
title: translateService.translate('sdk.general.error.timeout.title'),
|
|
29520
|
-
content: translateService.translate('sdk.general.error.timeout.content'),
|
|
29521
|
-
confirm: translateService.translate('sdk.general.confirm'),
|
|
29522
|
-
onConfirm: () => {
|
|
29523
|
-
observer.next(true);
|
|
29524
|
-
observer.complete();
|
|
29525
|
-
}
|
|
29526
|
-
});
|
|
29527
|
-
}))).subscribe(() => {
|
|
29528
|
-
timeout$.next({
|
|
29529
|
-
isSuccess: false,
|
|
29530
|
-
code: `${core.ErrorCode.ID_RECOGNITION_TIMEOUT}`,
|
|
29531
|
-
message: new core.AuthmeError(core.ErrorCode.ID_RECOGNITION_TIMEOUT).message,
|
|
29532
|
-
data: {}
|
|
29533
|
-
});
|
|
29534
|
-
});
|
|
29535
|
-
}
|
|
29536
29432
|
function initOptions() {
|
|
29537
29433
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29538
29434
|
const themeConfigApi = yield config.getOptionConfig();
|
|
@@ -29568,7 +29464,6 @@ function startLiveness(config) {
|
|
|
29568
29464
|
// 需要等到介紹頁完成後,才開始執行 config.init (engine init 流程)。
|
|
29569
29465
|
const init$ = rxjs.defer(() => config.init()).pipe(rxjs.tap(res => {
|
|
29570
29466
|
livenessConfig = res.parameters;
|
|
29571
|
-
expiredIn = res.expiredIn;
|
|
29572
29467
|
}), rxjs.catchError(error => __awaiter(this, void 0, void 0, function* () {
|
|
29573
29468
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
29574
29469
|
const EVENT_NAME_ERROR_CODE = 7;
|
|
@@ -29626,6 +29521,9 @@ function startLiveness(config) {
|
|
|
29626
29521
|
})));
|
|
29627
29522
|
const applyTextByResult = result => {
|
|
29628
29523
|
// debugLog('fas-response', result);
|
|
29524
|
+
if (lastFasStatus !== result.eStatus) {
|
|
29525
|
+
lastFasStatus = result.eStatus;
|
|
29526
|
+
}
|
|
29629
29527
|
sendStatusDescription$2(fasRecognitionResultMapping(result.eStatus));
|
|
29630
29528
|
switch (result.eStatus) {
|
|
29631
29529
|
case liveness.FasRecognitionResult.NoFace:
|
|
@@ -29683,6 +29581,10 @@ function startLiveness(config) {
|
|
|
29683
29581
|
uiComponentLiveness.statusText.textContent = translateService.translate('sdk.general.verify.success');
|
|
29684
29582
|
setBorderStatus('pass');
|
|
29685
29583
|
break;
|
|
29584
|
+
case liveness.FasRecognitionResult.Motion:
|
|
29585
|
+
uiComponentLiveness.statusText.textContent = translateService.translate('sdk.liveness.detection.motion');
|
|
29586
|
+
setBorderStatus('pass');
|
|
29587
|
+
break;
|
|
29686
29588
|
case liveness.FasRecognitionResult.Pass:
|
|
29687
29589
|
if (result.eStage === liveness.EAuthMeFASServiceStage.Scale) {
|
|
29688
29590
|
setBorderStatus(null);
|
|
@@ -29774,9 +29676,6 @@ function startLiveness(config) {
|
|
|
29774
29676
|
}
|
|
29775
29677
|
setCorrectViewHeight();
|
|
29776
29678
|
}), rxjs.switchMap(() => init$), rxjs.switchMap(() => requestCamera$), rxjs.tap(() => {
|
|
29777
|
-
if (expiredIn) {
|
|
29778
|
-
sdkFlowTimeout = makeSDKFlowTimeout(expiredIn);
|
|
29779
|
-
}
|
|
29780
29679
|
engineInited = true;
|
|
29781
29680
|
}), rxjs.switchMap(() => rxjs.from(util.waitTime(1)).pipe(rxjs.tap(() => uiComponentBasic.videoContainer.style.zIndex = '101'))), rxjs.switchMap(() => rxjs.from(util.getCanvasSize(uiComponentBasic.video))), rxjs.switchMap(canvasSizeInfo => rxjs.from(config.onStart(canvasSizeInfo)).pipe(rxjs.tap(params => {
|
|
29782
29681
|
const {
|
|
@@ -29826,26 +29725,16 @@ function startLiveness(config) {
|
|
|
29826
29725
|
// ),
|
|
29827
29726
|
rxjs.filter(({
|
|
29828
29727
|
result
|
|
29829
|
-
}) => (result.eStatus === liveness.FasRecognitionResult.Pass || result.eStatus === liveness.FasRecognitionResult.Failed) && result.eStage === liveness.EAuthMeFASServiceStage.Done), rxjs.
|
|
29830
|
-
|
|
29728
|
+
}) => (result.eStatus === liveness.FasRecognitionResult.Pass || result.eStatus === liveness.FasRecognitionResult.Failed || result.eStatus === liveness.FasRecognitionResult.DepthFake) && result.eStage === liveness.EAuthMeFASServiceStage.Done), rxjs.take(1),
|
|
29729
|
+
// 只處理第一個成功結果,防止 engine 繼續運行導致重複上傳
|
|
29730
|
+
rxjs.map(data => {
|
|
29731
|
+
// DepthFake 和 Failed 都視為失敗
|
|
29732
|
+
const isSuccess = data.result.eStatus === liveness.FasRecognitionResult.Pass;
|
|
29831
29733
|
return Object.assign(Object.assign({}, data), {
|
|
29832
|
-
isSuccess
|
|
29734
|
+
isSuccess
|
|
29833
29735
|
});
|
|
29834
|
-
}), rxjs.
|
|
29835
|
-
|
|
29836
|
-
// switchMap((resp) =>
|
|
29837
|
-
// window.navigator.onLine
|
|
29838
|
-
// ? of(resp)
|
|
29839
|
-
// : from(
|
|
29840
|
-
// asyncOnLineShowErrorMessage(
|
|
29841
|
-
// translateService.translate('sdk.general.error.alert.offline'),
|
|
29842
|
-
// translateService.translate('sdk.general.error.retry'),
|
|
29843
|
-
// false
|
|
29844
|
-
// )
|
|
29845
|
-
// ).pipe(map(() => resp))
|
|
29846
|
-
// ),
|
|
29847
|
-
rxjs.tap(res => {
|
|
29848
|
-
// console.log('res', res);
|
|
29736
|
+
}))))), rxjs.tap(res => {
|
|
29737
|
+
// Engine timeout 會在 res.isSuccess 為 false 時發生
|
|
29849
29738
|
if (res.isSuccess) {
|
|
29850
29739
|
sendStatusDescription$2(core.StatusDescription.UploadingStart);
|
|
29851
29740
|
util.startSpinner({
|
|
@@ -29854,12 +29743,10 @@ function startLiveness(config) {
|
|
|
29854
29743
|
backgroundOpaque: true
|
|
29855
29744
|
});
|
|
29856
29745
|
} else {
|
|
29857
|
-
|
|
29746
|
+
// Engine 返回 timeout 或其他失敗狀態
|
|
29858
29747
|
throw new core.AuthmeError(core.ErrorCode.ID_RECOGNITION_TIMEOUT);
|
|
29859
29748
|
}
|
|
29860
|
-
}), rxjs.filter(res => res.isSuccess),
|
|
29861
|
-
// **只執行成功的情況**
|
|
29862
|
-
rxjs.switchMap(() => rxjs.defer(() => config.onSuccess())));
|
|
29749
|
+
}), rxjs.filter(res => res.isSuccess), rxjs.switchMap(() => rxjs.defer(() => config.onSuccess())));
|
|
29863
29750
|
return rxjs.of({}).pipe(() => {
|
|
29864
29751
|
eventListenerService$2.start();
|
|
29865
29752
|
setStatusView$1(core.StatusView.Init);
|
|
@@ -29962,11 +29849,6 @@ function startLiveness(config) {
|
|
|
29962
29849
|
} finally {
|
|
29963
29850
|
uiComponentBasic.video.srcObject = null;
|
|
29964
29851
|
}
|
|
29965
|
-
// if (sdkFlowTimeout) clearTimeout(sdkFlowTimeout);
|
|
29966
|
-
if (sdkFlowTimeout) {
|
|
29967
|
-
sdkFlowTimeout.unsubscribe();
|
|
29968
|
-
sdkFlowTimeout = null;
|
|
29969
|
-
}
|
|
29970
29852
|
eventListenerService$2.stop();
|
|
29971
29853
|
uiComponentBasic.container.remove();
|
|
29972
29854
|
unsubscribe$.next();
|
|
@@ -30491,18 +30373,20 @@ const renderOCRMask = params => {
|
|
|
30491
30373
|
borderOpacity = params.strokeOpacity;
|
|
30492
30374
|
}
|
|
30493
30375
|
if (params.type === 'bordered') {
|
|
30494
|
-
|
|
30495
|
-
|
|
30496
|
-
|
|
30497
|
-
|
|
30498
|
-
|
|
30499
|
-
|
|
30500
|
-
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30376
|
+
// 使用 path 畫傾斜的四邊形框線(支援防偽的傾斜效果)
|
|
30377
|
+
rectOfFrame.setAttribute('visibility', 'hidden');
|
|
30378
|
+
pathOfFrame.setAttribute('visibility', 'visible');
|
|
30379
|
+
// 計算四個角的座標
|
|
30380
|
+
const points = newCardPoints.map(p => ({
|
|
30381
|
+
x: p.x * windowWidth,
|
|
30382
|
+
y: p.y * windowHeight
|
|
30383
|
+
}));
|
|
30384
|
+
// 使用 path 畫閉合的四邊形
|
|
30385
|
+
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`;
|
|
30386
|
+
pathOfFrame.setAttribute('d', dBordered);
|
|
30387
|
+
pathOfFrame.setAttribute('stroke-opacity', borderOpacity.toString());
|
|
30388
|
+
pathOfFrame.setAttribute('stroke-width', '3');
|
|
30389
|
+
svg.appendChild(pathOfFrame);
|
|
30506
30390
|
} else {
|
|
30507
30391
|
rectOfFrame.setAttribute('visibility', 'hidden');
|
|
30508
30392
|
pathOfFrame.setAttribute('visibility', 'visible');
|
|
@@ -30592,11 +30476,10 @@ const renderOCRMask = params => {
|
|
|
30592
30476
|
path.setAttribute('d', d);
|
|
30593
30477
|
// Set the same path for the border to match the mask exactly
|
|
30594
30478
|
borderPath.setAttribute('d', d);
|
|
30479
|
+
// Anti-fraud 模式下:隱藏 pathOfFrame 和 rectOfFrame,只顯示 borderPath(傾斜的框)
|
|
30595
30480
|
if (antiFraud) {
|
|
30596
|
-
|
|
30597
|
-
|
|
30598
|
-
mirrored: _mirrored
|
|
30599
|
-
});
|
|
30481
|
+
pathOfFrame.setAttribute('visibility', 'hidden');
|
|
30482
|
+
rectOfFrame.setAttribute('visibility', 'hidden');
|
|
30600
30483
|
}
|
|
30601
30484
|
};
|
|
30602
30485
|
function setBorderSuccess(color, opacity) {
|
|
@@ -30619,12 +30502,13 @@ const renderOCRMask = params => {
|
|
|
30619
30502
|
if (borderType === 'bordered') {
|
|
30620
30503
|
rectOfFrame.setAttribute('stroke', color);
|
|
30621
30504
|
rectOfFrame.setAttribute('stroke-opacity', opacity.toString());
|
|
30622
|
-
borderPath.setAttribute('stroke', color);
|
|
30623
|
-
borderPath.setAttribute('stroke-opacity', opacity.toString());
|
|
30624
30505
|
} else {
|
|
30625
30506
|
pathOfFrame.setAttribute('stroke', color);
|
|
30626
30507
|
pathOfFrame.setAttribute('stroke-opacity', opacity.toString());
|
|
30627
30508
|
}
|
|
30509
|
+
// 同時更新 borderPath (用於 anti-fraud 模式的傾斜框線)
|
|
30510
|
+
borderPath.setAttribute('stroke', color);
|
|
30511
|
+
borderPath.setAttribute('stroke-opacity', opacity.toString());
|
|
30628
30512
|
}
|
|
30629
30513
|
function frameImage(faceMode, zIndex, base64, color, opacity) {
|
|
30630
30514
|
color = color !== null && color !== void 0 ? color : OcrFrame.imageColor;
|
|
@@ -31922,6 +31806,78 @@ const fraudScanIntroPage = arg => {
|
|
|
31922
31806
|
authmeContainer.appendChild(domModal);
|
|
31923
31807
|
};
|
|
31924
31808
|
|
|
31809
|
+
const popupView = arg => {
|
|
31810
|
+
const authmeContainer = document.querySelector('.authme-container');
|
|
31811
|
+
if (!authmeContainer) {
|
|
31812
|
+
console.error('modal: authmeContainer not found');
|
|
31813
|
+
return;
|
|
31814
|
+
}
|
|
31815
|
+
const uiThemeConfig = util.Storage.getItem('themeConfig');
|
|
31816
|
+
function removePopview() {
|
|
31817
|
+
var _a;
|
|
31818
|
+
(_a = document.querySelector('.video-container__popupview')) === null || _a === void 0 ? void 0 : _a.remove();
|
|
31819
|
+
}
|
|
31820
|
+
const domPopupView = document.createElement('div');
|
|
31821
|
+
const domPopupViewContainer = document.createElement('div');
|
|
31822
|
+
const domTitleContainer = document.createElement('div');
|
|
31823
|
+
const domTitle = document.createElement('div');
|
|
31824
|
+
const domContentContainer = document.createElement('div');
|
|
31825
|
+
const domContent = document.createElement('div');
|
|
31826
|
+
const domFooterContainer = document.createElement('div');
|
|
31827
|
+
const domConfirm = document.createElement('div');
|
|
31828
|
+
const domCancel = document.createElement('div');
|
|
31829
|
+
domPopupView.classList.add('video-container__popupview');
|
|
31830
|
+
domPopupViewContainer.classList.add('video-container__popupview-container');
|
|
31831
|
+
domTitleContainer.classList.add('video-container__popupview-title-container');
|
|
31832
|
+
domTitle.classList.add('video-container__popupview-title');
|
|
31833
|
+
domContentContainer.classList.add('video-container__popupview-content-container');
|
|
31834
|
+
domContent.classList.add('video-container__popupview-content');
|
|
31835
|
+
domFooterContainer.classList.add('video-container__popupview-footer-container');
|
|
31836
|
+
domConfirm.classList.add('video-container__popupview-confirm');
|
|
31837
|
+
domCancel.classList.add('video-container__popupview-cancel');
|
|
31838
|
+
domPopupViewContainer.style.backgroundColor = uiThemeConfig.popupView.backgroundColor;
|
|
31839
|
+
domPopupViewContainer.style.borderRadius = `${uiThemeConfig.popupView.cornerRadius}px`;
|
|
31840
|
+
domPopupViewContainer.style.width = `${parseFloat(uiThemeConfig.popupView.width) * 100}%`;
|
|
31841
|
+
util.uiThemeText(domTitle, uiThemeConfig.titleOne);
|
|
31842
|
+
util.uiThemeText(domContent, uiThemeConfig.bodyTwo);
|
|
31843
|
+
console.log('deviceType', uiThemeConfig.deviceType);
|
|
31844
|
+
if (uiThemeConfig.deviceType === 'mobile') {
|
|
31845
|
+
util.uiThemeSmallButton(domConfirm, uiThemeConfig.smallermMajorButton);
|
|
31846
|
+
} else {
|
|
31847
|
+
util.uiThemeButton(domConfirm, uiThemeConfig.majorButton);
|
|
31848
|
+
}
|
|
31849
|
+
if (arg.cancel) {
|
|
31850
|
+
util.uiThemeSmallButton(domCancel, uiThemeConfig.smallerMinorButton);
|
|
31851
|
+
domCancel.innerHTML = arg.cancel;
|
|
31852
|
+
domCancel.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31853
|
+
if (arg.onCancel) {
|
|
31854
|
+
arg.onCancel();
|
|
31855
|
+
}
|
|
31856
|
+
removePopview();
|
|
31857
|
+
}));
|
|
31858
|
+
}
|
|
31859
|
+
domConfirm.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31860
|
+
if (arg.onConfirm) {
|
|
31861
|
+
arg.onConfirm();
|
|
31862
|
+
}
|
|
31863
|
+
removePopview();
|
|
31864
|
+
}));
|
|
31865
|
+
domTitle.innerHTML = arg.title;
|
|
31866
|
+
domContent.innerHTML = arg.content;
|
|
31867
|
+
domConfirm.innerHTML = arg.confirm;
|
|
31868
|
+
domPopupViewContainer.appendChild(domTitleContainer);
|
|
31869
|
+
domTitleContainer.appendChild(domTitle);
|
|
31870
|
+
domPopupViewContainer.appendChild(domContentContainer);
|
|
31871
|
+
domContentContainer.appendChild(domContent);
|
|
31872
|
+
domPopupViewContainer.appendChild(domFooterContainer);
|
|
31873
|
+
if (arg.cancel) {
|
|
31874
|
+
domFooterContainer.appendChild(domCancel);
|
|
31875
|
+
}
|
|
31876
|
+
domFooterContainer.appendChild(domConfirm);
|
|
31877
|
+
domPopupView.appendChild(domPopupViewContainer);
|
|
31878
|
+
authmeContainer.appendChild(domPopupView);
|
|
31879
|
+
};
|
|
31880
|
+
|
|
31925
31881
|
const ocrResultModal = arg => {
|
|
31926
31882
|
let modifiedDetails = {};
|
|
31927
31883
|
const authmeContainer = document.querySelector('.authme-container');
|
|
@@ -32142,6 +32098,10 @@ const ocrResultModal = arg => {
|
|
|
32142
32098
|
}
|
|
32143
32099
|
|
|
32144
32100
|
function parseRegex(input, forceUnicode = false) {
|
|
32101
|
+
// 處理 undefined 或 null 輸入
|
|
32102
|
+
if (!input) {
|
|
32103
|
+
return null;
|
|
32104
|
+
}
|
|
32145
32105
|
try {
|
|
32146
32106
|
let pattern = input;
|
|
32147
32107
|
let flags = '';
|
|
@@ -32436,6 +32396,7 @@ function startOCR(config) {
|
|
|
32436
32396
|
fraudRetryTimes: 1,
|
|
32437
32397
|
fraudTimeout: 52,
|
|
32438
32398
|
fraudMaxFps: 2,
|
|
32399
|
+
ocrMaxFps: 2,
|
|
32439
32400
|
captureTimeout: config.ocrConfig.captureTimeout
|
|
32440
32401
|
};
|
|
32441
32402
|
let cardSizeInfo = {
|
|
@@ -32729,9 +32690,9 @@ function startOCR(config) {
|
|
|
32729
32690
|
uiComponentOCRMask.setCardBorderColor('error');
|
|
32730
32691
|
break;
|
|
32731
32692
|
case idRecognition.EAuthMeIDCardAntiFraudStatus.Error:
|
|
32732
|
-
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.error.
|
|
32733
|
-
util.showErrorMessage(translateService.translate('sdk.general.error.alert.serverError'), false);
|
|
32693
|
+
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.serverError');
|
|
32734
32694
|
sendStatusDescription$1(core.StatusDescription.Error);
|
|
32695
|
+
uiComponentOCRMask.setCardBorderColor('error');
|
|
32735
32696
|
break;
|
|
32736
32697
|
case idRecognition.EAuthMeIDCardAntiFraudStatus.NeedDeformationFrontal:
|
|
32737
32698
|
needDeformationCount++;
|
|
@@ -32825,16 +32786,32 @@ function startOCR(config) {
|
|
|
32825
32786
|
return ocrSendFrameAnimation;
|
|
32826
32787
|
}
|
|
32827
32788
|
}), rxjs.map(x => x.result), rxjs.concatMap(x => __awaiter(this, void 0, void 0, function* () {
|
|
32789
|
+
// V9: 檢測 stage 變化,如果 stage 變化了,先顯示 pass 顏色
|
|
32790
|
+
// V9 Engine 不再返回 StagePass 狀態,而是直接切換到下一個 stage
|
|
32791
|
+
const stageChanged = x.eStage !== currentAntiFraudStage && currentAntiFraudStage !== undefined && x.eStage !== idRecognition.EAuthMeIDCardAntiFraudStage.Done;
|
|
32792
|
+
if (stageChanged) {
|
|
32793
|
+
// 顯示 pass 顏色表示上一個 stage 完成
|
|
32794
|
+
uiComponentOCRMask.setCardBorderColor('pass');
|
|
32795
|
+
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.stagePass');
|
|
32796
|
+
previosAntiFraudAnimateTime = Date.now();
|
|
32797
|
+
// 等待一段時間讓用戶看到顏色變化
|
|
32798
|
+
yield new Promise(resolve => setTimeout(resolve, 1500));
|
|
32799
|
+
yield stopAnimate();
|
|
32800
|
+
needDeformationCount = 0;
|
|
32801
|
+
previosAntiFraudAnimateTime = 0;
|
|
32802
|
+
// 更新 currentAntiFraudStage 並設定新的 stage 框線
|
|
32803
|
+
currentAntiFraudStage = x.eStage;
|
|
32804
|
+
// 調用 cardRotateByStage 更新框線位置,但傳入 mandatoryRotate = true
|
|
32805
|
+
yield cardRotateByStage(x.eStage, true);
|
|
32806
|
+
// 跳過這一幀的 applyTextByResult,直接返回
|
|
32807
|
+
return x;
|
|
32808
|
+
}
|
|
32828
32809
|
yield applyTextByResult(x);
|
|
32829
32810
|
cardRotateByStage(x.eStage);
|
|
32830
32811
|
if (isTutorialFinish) {
|
|
32831
32812
|
// 文字更新移到 cardRotateAnimationProcess 中,確保與動畫同步
|
|
32832
32813
|
x.eStage;
|
|
32833
32814
|
}
|
|
32834
|
-
if (x.eStage !== currentAntiFraudStage) {
|
|
32835
|
-
needDeformationCount = 0;
|
|
32836
|
-
previosAntiFraudAnimateTime = 0;
|
|
32837
|
-
}
|
|
32838
32815
|
if (!uiThemeConfig.isFraudAnimationLoadingPageEnabled) {
|
|
32839
32816
|
// if (config.ocrConfig.disableTutorial) {
|
|
32840
32817
|
util.stopSpinner();
|
|
@@ -32919,6 +32896,12 @@ function startOCR(config) {
|
|
|
32919
32896
|
sendStatusDescription$1(core.StatusDescription.Reflective);
|
|
32920
32897
|
uiComponentOCRMask.setCardBorderColor('error');
|
|
32921
32898
|
break;
|
|
32899
|
+
case idRecognition.EAuthMeCardOCRStatus.Gray:
|
|
32900
|
+
// V9 新增: Gray 狀態表示灰階或顏色異常,顯示與 Blur 類似的提示
|
|
32901
|
+
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
|
|
32902
|
+
sendStatusDescription$1(core.StatusDescription.Gray);
|
|
32903
|
+
uiComponentOCRMask.setCardBorderColor('error');
|
|
32904
|
+
break;
|
|
32922
32905
|
case idRecognition.EAuthMeCardOCRStatus.Blur:
|
|
32923
32906
|
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
|
|
32924
32907
|
sendStatusDescription$1(core.StatusDescription.Blur);
|
|
@@ -32946,6 +32929,19 @@ function startOCR(config) {
|
|
|
32946
32929
|
sendStatusDescription$1(core.StatusDescription.Reflective);
|
|
32947
32930
|
uiComponentOCRMask.setCardBorderColor('error');
|
|
32948
32931
|
break;
|
|
32932
|
+
case idRecognition.EAuthMeMRZServiceStatus.Blur:
|
|
32933
|
+
// V9 新增: MRZ Blur 狀態
|
|
32934
|
+
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.blur');
|
|
32935
|
+
sendStatusDescription$1(core.StatusDescription.Blur);
|
|
32936
|
+
uiComponentOCRMask.setCardBorderColor('error');
|
|
32937
|
+
blurCount++;
|
|
32938
|
+
break;
|
|
32939
|
+
case idRecognition.EAuthMeMRZServiceStatus.MRZNotFound:
|
|
32940
|
+
// V9 新增: MRZ區域未找到(卡片存在但無法識別MRZ區)
|
|
32941
|
+
uiComponentOCR.statusText.textContent = translateService.translate('sdk.general.verify.error.wrongCardType');
|
|
32942
|
+
sendStatusDescription$1(core.StatusDescription.WrongCardType);
|
|
32943
|
+
uiComponentOCRMask.setCardBorderColor('error');
|
|
32944
|
+
break;
|
|
32949
32945
|
}
|
|
32950
32946
|
if (blurCount === 2) {
|
|
32951
32947
|
showCameraSwitchButton(deviceMetas);
|
|
@@ -33078,7 +33074,12 @@ function startOCR(config) {
|
|
|
33078
33074
|
const ctx = canvas.getContext('2d', {
|
|
33079
33075
|
willReadFrequently: true
|
|
33080
33076
|
});
|
|
33081
|
-
return source$.pipe(rxjs.mergeMap(() => rxjs.animationFrames().pipe(limitFPS(fps), rxjs.filter(() => received && !ocrSendFrameAnimation), rxjs.tap(() => received = false), rxjs.tap(() => util.clearCanvas(canvas)), rxjs.map(() => util.getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), rxjs.
|
|
33077
|
+
return source$.pipe(rxjs.mergeMap(() => rxjs.animationFrames().pipe(limitFPS(fps), rxjs.filter(() => received && !ocrSendFrameAnimation), rxjs.tap(() => received = false), rxjs.tap(() => util.clearCanvas(canvas)), rxjs.map(() => util.getImageData(canvas, ctx, video, canvasSizeInfo, bas64Format, imageType)), rxjs.tap(imageData => {
|
|
33078
|
+
// 如果 imageData 為 null(canvas 尺寸無效),重置 received 讓下一幀可以繼續
|
|
33079
|
+
if (imageData === null) {
|
|
33080
|
+
received = true;
|
|
33081
|
+
}
|
|
33082
|
+
}), rxjs.filter(imageData => imageData !== null), rxjs.mergeMap(imageData => rxjs.from(frameCallback(imageData.data, imageData.base64, cardType, type)).pipe(rxjs.catchError(e => {
|
|
33082
33083
|
// send to fast, ignore
|
|
33083
33084
|
if (e instanceof core.AuthmeError && e.code === core.ErrorCode.RECOGNITION_NOT_AVAILABLE) {
|
|
33084
33085
|
return rxjs.EMPTY;
|
|
@@ -33090,7 +33091,7 @@ function startOCR(config) {
|
|
|
33090
33091
|
})), rxjs.tap(() => received = true))))));
|
|
33091
33092
|
};
|
|
33092
33093
|
const autoCapture = canvasSizeInfo => {
|
|
33093
|
-
return rxjs.of(canvasSizeInfo).pipe(handleOcrSendFrame(canvasSizeInfo, uiComponentOCR.image, uiComponentBasic.video, config.recognition,
|
|
33094
|
+
return rxjs.of(canvasSizeInfo).pipe(handleOcrSendFrame(canvasSizeInfo, uiComponentOCR.image, uiComponentBasic.video, config.recognition, ocrEngineConfig.ocrMaxFps, false, config.ocrConfig.resultImageFormat, cardType, type), rxjs.tap(x => applyTextByResult(x.result)), rxjs.filter(({
|
|
33094
33095
|
result
|
|
33095
33096
|
}) => result.eStatus === idRecognition.EAuthMeCardOCRStatus.Pass || result.eStatus === idRecognition.EAuthMeMRZServiceStatus.Success), rxjs.take(1), rxjs.tap(() => {
|
|
33096
33097
|
if (countdownCaptureTimer && countdownCaptureTimer.end && !countdownCaptureTimer.end()) {
|
|
@@ -33108,38 +33109,47 @@ function startOCR(config) {
|
|
|
33108
33109
|
util.hideElement(uiComponentOCR.scanAnimationContainer);
|
|
33109
33110
|
}), rxjs.map(() => resp))), rxjs.switchMap(({
|
|
33110
33111
|
result
|
|
33111
|
-
}) =>
|
|
33112
|
-
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
|
|
33112
|
+
}) => {
|
|
33113
|
+
// V9: 檢查卡片圖像是否可用
|
|
33114
|
+
const cardResult = result;
|
|
33115
|
+
const hasValidImage = cardResult.imageData && cardResult.iWidth > 0 && cardResult.iHeight > 0;
|
|
33116
|
+
if (!hasValidImage) {
|
|
33117
|
+
console.warn('[OCR] Card image not available, skipping confirm page. iWidth:', cardResult.iWidth, 'iHeight:', cardResult.iHeight, 'imageData:', cardResult.imageData ? 'exists' : 'null');
|
|
33118
|
+
}
|
|
33119
|
+
const shouldSkipConfirm = type === idRecognition.EAuthMeCardClass.Passport && config.ocrConfig.disablePassportConfirm || !config.ocrConfig.confirmPageEnabled || !hasValidImage; // V9: 如果沒有卡片圖像,跳過確認頁
|
|
33120
|
+
return rxjs.from(shouldSkipConfirm ? rxjs.of(false) : checkConfirmImage(cardResult.imageData, cardResult.iWidth, cardResult.iHeight)).pipe(rxjs.switchMap(needRetry => {
|
|
33121
|
+
if (countdownCaptureTimer && countdownCaptureTimer.end) {
|
|
33122
|
+
if (needRetry && !countdownCaptureTimer.end()) {
|
|
33123
|
+
countdownCaptureTimer = countdownTimer(captureTimeoutTimer, () => {
|
|
33124
|
+
util.showElement(captureBtn);
|
|
33125
|
+
toastManualCapture = toast({
|
|
33126
|
+
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'),
|
|
33127
|
+
transition: true
|
|
33128
|
+
});
|
|
33119
33129
|
});
|
|
33120
|
-
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33130
|
+
countdownCaptureTimer.init();
|
|
33131
|
+
}
|
|
33132
|
+
if (!needRetry) {
|
|
33133
|
+
countdownCaptureTimer.clear();
|
|
33134
|
+
util.hideElement(captureBtn);
|
|
33135
|
+
}
|
|
33126
33136
|
}
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33136
|
-
})
|
|
33137
|
-
|
|
33138
|
-
|
|
33139
|
-
|
|
33140
|
-
|
|
33141
|
-
|
|
33142
|
-
})
|
|
33137
|
+
util.startSpinner({
|
|
33138
|
+
text: translateService.translate('sdk.general.uploading'),
|
|
33139
|
+
statement: translateService.translate('sdk.general.footer'),
|
|
33140
|
+
backgroundOpaque: true
|
|
33141
|
+
});
|
|
33142
|
+
return needRetry ? rxjs.of(true) : rxjs.from(config.confirmImage({
|
|
33143
|
+
type,
|
|
33144
|
+
cardType
|
|
33145
|
+
})).pipe(rxjs.tap(() => sendStatusAction$1(core.StatusAction.Uploading)), rxjs.map(confirmResp => !confirmResp));
|
|
33146
|
+
}), rxjs.tap(() => {
|
|
33147
|
+
// hideElement(uiComponentOCR.confirmImageContainer);
|
|
33148
|
+
util.hideElement(uiComponentOCR.confirmContainer);
|
|
33149
|
+
util.stopSpinner();
|
|
33150
|
+
showVideoElement();
|
|
33151
|
+
}), rxjs.switchMap(needRetry => needRetry ? recognition(true) : rxjs.of(true)));
|
|
33152
|
+
}));
|
|
33143
33153
|
};
|
|
33144
33154
|
const recognition = retry => {
|
|
33145
33155
|
return init(retry).pipe(rxjs.tap(() => {
|
|
@@ -33431,7 +33441,7 @@ function startOCR(config) {
|
|
|
33431
33441
|
const cardType = currentType('get', null).cardType;
|
|
33432
33442
|
const ctx = uiComponentOCR.image.getContext('2d');
|
|
33433
33443
|
const imageData = util.getImageData(uiComponentOCR.image, ctx, uiComponentBasic.video, canvasSizeInfo, false, config.ocrConfig.resultImageFormat);
|
|
33434
|
-
const imageBlob = util.UintArrayToBlob(canvasSizeInfo.width, canvasSizeInfo.height, imageData.data);
|
|
33444
|
+
const imageBlob = imageData ? util.UintArrayToBlob(canvasSizeInfo.width, canvasSizeInfo.height, imageData.data) : undefined;
|
|
33435
33445
|
const cancelResultObj = {
|
|
33436
33446
|
isSuccess: false,
|
|
33437
33447
|
code: `${core.ErrorCode.USER_CANCEL}`,
|
|
@@ -33782,7 +33792,7 @@ function startOCR(config) {
|
|
|
33782
33792
|
function cardRotateByStage(stage, mandatoryRotate = false, point) {
|
|
33783
33793
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33784
33794
|
if ((stage === idRecognition.EAuthMeIDCardAntiFraudStage.Done || stage === currentAntiFraudStage) && mandatoryRotate === false) return;
|
|
33785
|
-
const cardMatchROI = point ? point : config.getCardMatchROI ? yield config.getCardMatchROI() : null;
|
|
33795
|
+
const cardMatchROI = point ? point : config.getCardMatchROI ? yield config.getCardMatchROI(stage) : null;
|
|
33786
33796
|
if (!cardMatchROI || cardMatchROI.length == 0) throw new core.AuthmeError(core.ErrorCode.SDK_INTERNAL_ERROR, 'getCardMatchROI is null');
|
|
33787
33797
|
uiComponentOCRMask.setCardBorderColor('error');
|
|
33788
33798
|
// 只有左右翻轉時需要正規化座標(確保左右邊平行)
|
|
@@ -34516,6 +34526,11 @@ class LivenessVerifyModule {
|
|
|
34516
34526
|
}
|
|
34517
34527
|
}
|
|
34518
34528
|
|
|
34529
|
+
/**
|
|
34530
|
+
* Debug flag for FAS (Face Authentication Service) encryption flow.
|
|
34531
|
+
* Set to true to enable detailed logging for debugging encryption issues.
|
|
34532
|
+
*/
|
|
34533
|
+
const DEBUG_FAS = false;
|
|
34519
34534
|
function handleUploadError$1(_error) {
|
|
34520
34535
|
return new Promise(resolve => {
|
|
34521
34536
|
util.uploadModal({
|
|
@@ -34553,12 +34568,18 @@ class LivenessModule {
|
|
|
34553
34568
|
let id = '';
|
|
34554
34569
|
let pubKey = '';
|
|
34555
34570
|
let shouldEncrypt = false;
|
|
34571
|
+
// 保存 API 返回的參數,避免在 onStart 中被覆蓋
|
|
34572
|
+
let apiTimeoutSec;
|
|
34573
|
+
let apiFasThreshold;
|
|
34556
34574
|
const encryptDataBase64 = data => __awaiter(this, void 0, void 0, function* () {
|
|
34557
34575
|
const dataString = JSON.stringify(data);
|
|
34558
34576
|
const encoder = new TextEncoder();
|
|
34559
34577
|
const uint8Array = encoder.encode(dataString);
|
|
34560
|
-
// TODO check encrypt function
|
|
34561
34578
|
const resultEncrypt = yield this.fasService.encryptBlob(uint8Array, pubKey);
|
|
34579
|
+
if (!resultEncrypt) {
|
|
34580
|
+
console.error('[encryptDataBase64] encryptBlob returned null/empty');
|
|
34581
|
+
throw new core.AuthmeError(core.ErrorCode.SDK_INTERNAL_ERROR, 'encryptBlob failed');
|
|
34582
|
+
}
|
|
34562
34583
|
return resultEncrypt;
|
|
34563
34584
|
});
|
|
34564
34585
|
const handleUpload = (id, frameList, resultList, meta, config, shouldEncrypt, encryptDataBase64) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -34580,7 +34601,6 @@ class LivenessModule {
|
|
|
34580
34601
|
try {
|
|
34581
34602
|
const result = yield rxjs.firstValueFrom(yield startLiveness({
|
|
34582
34603
|
getOptionConfig: () => __awaiter(this, void 0, void 0, function* () {
|
|
34583
|
-
console.log('config.deviceType', config.deviceType);
|
|
34584
34604
|
const res = yield liveness.LivenessAPI.IdentityVerification.option(config.deviceType);
|
|
34585
34605
|
const themeId = res.themeId;
|
|
34586
34606
|
if (!themeId) {
|
|
@@ -34595,6 +34615,10 @@ class LivenessModule {
|
|
|
34595
34615
|
id = resp.id;
|
|
34596
34616
|
pubKey = resp.parameters.pubKey;
|
|
34597
34617
|
shouldEncrypt = (_a = resp.shouldEncrypt) !== null && _a !== void 0 ? _a : shouldEncrypt;
|
|
34618
|
+
if (DEBUG_FAS) ;
|
|
34619
|
+
// 根據 shouldEncrypt 決定是否設置 report key
|
|
34620
|
+
// shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
|
|
34621
|
+
// shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
|
|
34598
34622
|
if (!shouldEncrypt) {
|
|
34599
34623
|
yield this.fasService.setPublicKeyForJson(pubKey);
|
|
34600
34624
|
} else {
|
|
@@ -34609,8 +34633,11 @@ class LivenessModule {
|
|
|
34609
34633
|
// fRight: 0.7,
|
|
34610
34634
|
// fBottom: 0.7,
|
|
34611
34635
|
// };
|
|
34612
|
-
|
|
34613
|
-
|
|
34636
|
+
// 保存 API 參數,在 onStart 中使用
|
|
34637
|
+
apiTimeoutSec = resp.parameters.fasTimeout || params.timeoutSec;
|
|
34638
|
+
apiFasThreshold = resp.parameters.fasThreshold || params.fFASTh;
|
|
34639
|
+
params.timeoutSec = apiTimeoutSec;
|
|
34640
|
+
params.fFASTh = apiFasThreshold;
|
|
34614
34641
|
yield this.fasService.setParams(params);
|
|
34615
34642
|
yield this.fasService.setStage(resp.parameters.fasStages.map(x => `EAuthMeFASServiceStage_${x}`));
|
|
34616
34643
|
// return resp.parameters;
|
|
@@ -34653,8 +34680,15 @@ class LivenessModule {
|
|
|
34653
34680
|
params.faceROI.fRight = 1 - (1 - widthPercent) / 2;
|
|
34654
34681
|
params.faceROI.fTop = (1 - heightPercent) / 2;
|
|
34655
34682
|
params.faceROI.fBottom = 1 - (1 - heightPercent) / 2;
|
|
34656
|
-
|
|
34683
|
+
// 保留 API 設定的 timeout 和 threshold,避免被 getParams() 的預設值覆蓋
|
|
34684
|
+
if (apiTimeoutSec !== undefined) {
|
|
34685
|
+
params.timeoutSec = apiTimeoutSec;
|
|
34686
|
+
}
|
|
34687
|
+
if (apiFasThreshold !== undefined) {
|
|
34688
|
+
params.fFASTh = apiFasThreshold;
|
|
34689
|
+
}
|
|
34657
34690
|
yield this.fasService.setFrameSize(frameWidth, frameHeight);
|
|
34691
|
+
yield this.fasService.setParams(params);
|
|
34658
34692
|
yield this.fasService.startSession();
|
|
34659
34693
|
return params;
|
|
34660
34694
|
}),
|
|
@@ -34809,12 +34843,27 @@ class LivenessModule {
|
|
|
34809
34843
|
data: meta
|
|
34810
34844
|
};
|
|
34811
34845
|
if (shouldEncrypt) {
|
|
34812
|
-
|
|
34846
|
+
// shouldEncrypt = true 時,沒有設置 report key,
|
|
34847
|
+
// getReport() 返回的是 base64 編碼的未加密 JSON
|
|
34848
|
+
// 需要先 atob 解碼成純 JSON 字串(保持為字串,與 Android SDK 一致)
|
|
34849
|
+
// 然後用 encryptDataBase64 加密整個 postData
|
|
34850
|
+
try {
|
|
34851
|
+
const decodedMeta = atob(meta);
|
|
34852
|
+
if (DEBUG_FAS) ;
|
|
34853
|
+
postData.data = decodedMeta; // 保持為字串,與 Android 一致
|
|
34854
|
+
} catch (e) {
|
|
34855
|
+
console.error('[uploadMeta] atob failed:', e);
|
|
34856
|
+
// 如果 atob 失敗,可能已經是純 JSON 字串,保持原樣
|
|
34857
|
+
}
|
|
34858
|
+
|
|
34859
|
+
const encrypted = yield encryptDataBase64(postData);
|
|
34813
34860
|
return liveness.LivenessAPI.IdentityVerification.uploadMeta({
|
|
34814
34861
|
id: id,
|
|
34815
|
-
encryptedBase64String:
|
|
34862
|
+
encryptedBase64String: encrypted
|
|
34816
34863
|
});
|
|
34817
34864
|
} else {
|
|
34865
|
+
// shouldEncrypt = false 時,已設置 report key,
|
|
34866
|
+
// getReport() 返回的是 engine 加密過的 base64 字符串
|
|
34818
34867
|
return liveness.LivenessAPI.IdentityVerification.uploadMeta(postData);
|
|
34819
34868
|
}
|
|
34820
34869
|
// return LivenessAPI.IdentityVerification.uploadMeta({
|
|
@@ -35116,13 +35165,19 @@ class MRZModule {
|
|
|
35116
35165
|
pubKey = resp.parameters.pubKey;
|
|
35117
35166
|
scanId = resp.scanId;
|
|
35118
35167
|
uploadFullFrame = (_c = (_b = resp.parameters.fraud) === null || _b === void 0 ? void 0 : _b.collectAllFrames) !== null && _c !== void 0 ? _c : config.uploadFullFrame;
|
|
35119
|
-
|
|
35168
|
+
// 根據 shouldEncrypt 決定是否設置 report key
|
|
35169
|
+
// shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
|
|
35170
|
+
// shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
|
|
35171
|
+
if (!shouldEncrypt) {
|
|
35120
35172
|
this.engine.setPublicKeyForJson(pubKey);
|
|
35173
|
+
} else {
|
|
35174
|
+
this.engine.setPublicKeyForJson('');
|
|
35121
35175
|
}
|
|
35122
35176
|
yield this.mrzService.init();
|
|
35123
35177
|
yield util.waitTime(100);
|
|
35124
35178
|
return Object.assign(Object.assign({}, resp.parameters), {
|
|
35125
|
-
expiredIn: resp.expiredIn
|
|
35179
|
+
expiredIn: resp.expiredIn,
|
|
35180
|
+
ocrMaxFps: 2
|
|
35126
35181
|
});
|
|
35127
35182
|
}),
|
|
35128
35183
|
ocrStart: (points, type, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode, cardType, retry = false) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -35187,29 +35242,44 @@ class MRZModule {
|
|
|
35187
35242
|
// 1. 最後結果統一使用 getFinalResult 取得的結果。
|
|
35188
35243
|
// 2. 由於 engine-lib 限制,getFinalResult 必須要在 stop 之後呼叫。
|
|
35189
35244
|
const finalResult = yield this.mrzService.getFinalResult();
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
|
|
35194
|
-
|
|
35195
|
-
|
|
35196
|
-
|
|
35197
|
-
|
|
35198
|
-
|
|
35199
|
-
|
|
35200
|
-
|
|
35201
|
-
|
|
35202
|
-
|
|
35203
|
-
|
|
35204
|
-
|
|
35205
|
-
|
|
35206
|
-
|
|
35207
|
-
|
|
35208
|
-
|
|
35209
|
-
|
|
35210
|
-
|
|
35211
|
-
|
|
35212
|
-
|
|
35245
|
+
if (finalResult) {
|
|
35246
|
+
const rawField = JSON.parse(yield this.mrzService.toJson(finalResult));
|
|
35247
|
+
// V9 API 回傳 snake_case 欄位,轉換為 camelCase 以匹配翻譯 key
|
|
35248
|
+
const snakeToCamelMap = {
|
|
35249
|
+
birth_date: 'birthDate',
|
|
35250
|
+
expiry_date: 'expiryDate',
|
|
35251
|
+
document_number: 'documentNumber',
|
|
35252
|
+
document_type: 'documentType',
|
|
35253
|
+
given_name: 'givenName',
|
|
35254
|
+
personal_number: 'personalNumber',
|
|
35255
|
+
sex: 'gender',
|
|
35256
|
+
surname: 'surname',
|
|
35257
|
+
nationality: 'nationality',
|
|
35258
|
+
country: 'country'
|
|
35259
|
+
};
|
|
35260
|
+
// 需要刪除的欄位 (check digit)
|
|
35261
|
+
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'];
|
|
35262
|
+
// 轉換欄位名稱並過濾 check digit
|
|
35263
|
+
latestTField = {};
|
|
35264
|
+
for (const [key, value] of Object.entries(rawField)) {
|
|
35265
|
+
if (fieldsToDelete.includes(key)) {
|
|
35266
|
+
continue; // 跳過 check digit 欄位
|
|
35267
|
+
}
|
|
35268
|
+
|
|
35269
|
+
const newKey = snakeToCamelMap[key] || key;
|
|
35270
|
+
latestTField[newKey] = value;
|
|
35271
|
+
}
|
|
35272
|
+
// 性別轉換
|
|
35273
|
+
if (latestTField === null || latestTField === void 0 ? void 0 : latestTField.gender) {
|
|
35274
|
+
switch (latestTField.gender) {
|
|
35275
|
+
case 'M':
|
|
35276
|
+
latestTField.gender = 'male';
|
|
35277
|
+
break;
|
|
35278
|
+
case 'F':
|
|
35279
|
+
latestTField.gender = 'female';
|
|
35280
|
+
break;
|
|
35281
|
+
}
|
|
35282
|
+
}
|
|
35213
35283
|
}
|
|
35214
35284
|
} else if (uploadFullFrame) {
|
|
35215
35285
|
const image = util.UintArrayToBlob(frameWidth, frameHeight, data, virtualCanvas);
|
|
@@ -35345,6 +35415,8 @@ class MRZModule {
|
|
|
35345
35415
|
getAntiFraudStageList: () => []
|
|
35346
35416
|
}));
|
|
35347
35417
|
} catch (error) {
|
|
35418
|
+
console.error('[MRZ Module] Error caught:', error);
|
|
35419
|
+
console.error('[MRZ Module] Error stack:', error === null || error === void 0 ? void 0 : error.stack);
|
|
35348
35420
|
const catchedError = error instanceof core.AuthmeError ? error : new core.AuthmeError(core.ErrorCode.SDK_INTERNAL_ERROR, error);
|
|
35349
35421
|
return {
|
|
35350
35422
|
isSuccess: false,
|
|
@@ -35357,8 +35429,98 @@ class MRZModule {
|
|
|
35357
35429
|
}
|
|
35358
35430
|
}
|
|
35359
35431
|
|
|
35360
|
-
|
|
35432
|
+
// V9: 卡片 OCR 參數常數名稱
|
|
35433
|
+
const F_IMAGE_BLUR_TH = 'fImageBlurTh';
|
|
35434
|
+
const F_IDCARD_COLOR_TH = 'fIdCardColorTh';
|
|
35435
|
+
const F_IMAGE_REFLECTIVE_TRIGGER_TH = 'fImageReflectiveTriggerTh';
|
|
35436
|
+
const F_IMAGE_REFLECTIVE_MASK_TH = 'fImageReflectiveMaskTh';
|
|
35437
|
+
const F_CARD_MATCH_TH = 'fCardMatchTh';
|
|
35438
|
+
const F_CARD_CLASSIFICATION_TH = 'fCardClassificationTh';
|
|
35439
|
+
// V9: 不同卡片類型的預設參數 (參考 Android SDK)
|
|
35440
|
+
// 邏輯說明:
|
|
35441
|
+
// - idCardColorTh: color_detect.score > threshold 為彩色 (Pass), < threshold 為灰階 (Gray)
|
|
35442
|
+
// - imageReflectiveTriggerTh: 反光分數 > threshold 判定為反光
|
|
35443
|
+
// - imageBlurTh: 模糊分數 > threshold 判定為模糊
|
|
35444
|
+
const CARD_OCR_PARAMS_MAP = {
|
|
35445
|
+
// 身分證正面: 高反光容限 (塑膠卡)
|
|
35446
|
+
TWN_IDCard_Front: {
|
|
35447
|
+
idCardColorTh: 15,
|
|
35448
|
+
imageBlurTh: 750,
|
|
35449
|
+
imageReflectiveTriggerTh: 1.05,
|
|
35450
|
+
imageReflectiveMaskTh: 1.5,
|
|
35451
|
+
cardMatchTh: 0.3,
|
|
35452
|
+
cardClassificationTh: 0.62655
|
|
35453
|
+
},
|
|
35454
|
+
// 身分證背面: 高反光容限 (塑膠卡)
|
|
35455
|
+
TWN_IDCard_Back: {
|
|
35456
|
+
idCardColorTh: 4.5,
|
|
35457
|
+
imageBlurTh: 750,
|
|
35458
|
+
imageReflectiveTriggerTh: 1.05,
|
|
35459
|
+
imageReflectiveMaskTh: 1.5,
|
|
35460
|
+
cardMatchTh: 0.3,
|
|
35461
|
+
cardClassificationTh: 0.62655
|
|
35462
|
+
},
|
|
35463
|
+
// 健保卡正面: 中等反光容限
|
|
35464
|
+
TWN_HealthCard_Front: {
|
|
35465
|
+
idCardColorTh: 10,
|
|
35466
|
+
imageBlurTh: 750,
|
|
35467
|
+
imageReflectiveTriggerTh: 0.35,
|
|
35468
|
+
imageReflectiveMaskTh: 0.45,
|
|
35469
|
+
cardMatchTh: 0.3,
|
|
35470
|
+
cardClassificationTh: 0.62655
|
|
35471
|
+
},
|
|
35472
|
+
// 駕照正面: 低反光觸發 (紙質)
|
|
35473
|
+
TWN_DriverLicense_Front: {
|
|
35474
|
+
idCardColorTh: 15,
|
|
35475
|
+
imageBlurTh: 750,
|
|
35476
|
+
imageReflectiveTriggerTh: 0.16,
|
|
35477
|
+
imageReflectiveMaskTh: 0.45,
|
|
35478
|
+
cardMatchTh: 0.3,
|
|
35479
|
+
cardClassificationTh: 0.62655
|
|
35480
|
+
},
|
|
35481
|
+
// 駕照背面: 極低反光觸發
|
|
35482
|
+
TWN_DriverLicense_Back: {
|
|
35483
|
+
idCardColorTh: 8,
|
|
35484
|
+
imageBlurTh: 600,
|
|
35485
|
+
imageReflectiveTriggerTh: 0.045,
|
|
35486
|
+
imageReflectiveMaskTh: 0.45,
|
|
35487
|
+
cardMatchTh: 0.75,
|
|
35488
|
+
cardClassificationTh: 0.62655
|
|
35489
|
+
},
|
|
35490
|
+
// 居留證正面
|
|
35491
|
+
TWN_ResidentCard_Front: {
|
|
35492
|
+
idCardColorTh: 0,
|
|
35493
|
+
imageBlurTh: 600,
|
|
35494
|
+
imageReflectiveTriggerTh: 0.045,
|
|
35495
|
+
imageReflectiveMaskTh: 0.45,
|
|
35496
|
+
cardMatchTh: 0.75,
|
|
35497
|
+
cardClassificationTh: 0.62655
|
|
35498
|
+
},
|
|
35499
|
+
// 居留證背面
|
|
35500
|
+
TWN_ResidentCard_Back: {
|
|
35501
|
+
idCardColorTh: 0,
|
|
35502
|
+
imageBlurTh: 600,
|
|
35503
|
+
imageReflectiveTriggerTh: 0.045,
|
|
35504
|
+
imageReflectiveMaskTh: 0.45,
|
|
35505
|
+
cardMatchTh: 0.75,
|
|
35506
|
+
cardClassificationTh: 0.62655
|
|
35507
|
+
}
|
|
35508
|
+
};
|
|
35509
|
+
// V9: 預設參數 (用於未定義的卡片類型)
|
|
35510
|
+
const DEFAULT_CARD_OCR_PARAMS = {
|
|
35511
|
+
idCardColorTh: 0,
|
|
35512
|
+
imageBlurTh: 750,
|
|
35513
|
+
imageReflectiveTriggerTh: 0.05,
|
|
35514
|
+
imageReflectiveMaskTh: 0.025,
|
|
35515
|
+
cardMatchTh: 0.3,
|
|
35516
|
+
cardClassificationTh: 0.62655
|
|
35517
|
+
};
|
|
35361
35518
|
const DEFAULT_ANTI_FRAUD_TIMEOUT = 40;
|
|
35519
|
+
// V9: OCR 結果需要過濾掉的欄位 (重複/內部欄位)
|
|
35520
|
+
// 這些欄位可能是內部檢核數字或與其他欄位重複
|
|
35521
|
+
const OCR_FIELDS_TO_FILTER = ['backSideId', 'reverseId', 'dob', 'firstName', 'lastName', 'fullName', 'optionalData2' // 內部欄位
|
|
35522
|
+
];
|
|
35523
|
+
|
|
35362
35524
|
function unionMerge(a, b) {
|
|
35363
35525
|
const entries = Object.entries(a).concat(Object.entries(b));
|
|
35364
35526
|
// union merge
|
|
@@ -35423,8 +35585,8 @@ function handleUploadError(error) {
|
|
|
35423
35585
|
});
|
|
35424
35586
|
});
|
|
35425
35587
|
}
|
|
35426
|
-
//
|
|
35427
|
-
const antiFraudStageListMap = [[engine.EAuthMeIDCardAntiFraudStage.Left, engine.EAuthMeIDCardAntiFraudStage.Right, engine.EAuthMeIDCardAntiFraudStage.Up, engine.EAuthMeIDCardAntiFraudStage.Down
|
|
35588
|
+
// V9: Engine 會自動重複 stages,只需要傳入基本的 4 個方向
|
|
35589
|
+
const antiFraudStageListMap = [[engine.EAuthMeIDCardAntiFraudStage.Left, engine.EAuthMeIDCardAntiFraudStage.Right, engine.EAuthMeIDCardAntiFraudStage.Up, engine.EAuthMeIDCardAntiFraudStage.Down]
|
|
35428
35590
|
// [
|
|
35429
35591
|
// EAuthMeIDCardAntiFraudStage.Left,
|
|
35430
35592
|
// EAuthMeIDCardAntiFraudStage.Right,
|
|
@@ -35742,12 +35904,17 @@ class OCRModule {
|
|
|
35742
35904
|
pubKey = resp.parameters.pubKey;
|
|
35743
35905
|
uploadFullFrame = (_c = (_b = resp.parameters.fraud) === null || _b === void 0 ? void 0 : _b.collectAllFrames) !== null && _c !== void 0 ? _c : config.uploadFullFrame;
|
|
35744
35906
|
fraudTimeout = (_e = (_d = resp.parameters.fraud) === null || _d === void 0 ? void 0 : _d.totalTimeout) !== null && _e !== void 0 ? _e : DEFAULT_ANTI_FRAUD_TIMEOUT;
|
|
35907
|
+
// 根據 shouldEncrypt 決定是否設置 report key
|
|
35908
|
+
// shouldEncrypt = true: 清空 report key,讓 getReport() 返回未加密的 base64,由 SDK 加密整個 payload
|
|
35909
|
+
// shouldEncrypt = false: 設置 report key,讓 getReport() 返回 engine 加密的 report
|
|
35745
35910
|
if (!shouldEncrypt) {
|
|
35746
35911
|
this.engine.setPublicKeyForJson(pubKey);
|
|
35747
35912
|
} else {
|
|
35748
35913
|
this.engine.setPublicKeyForJson('');
|
|
35749
35914
|
}
|
|
35750
35915
|
if (config.type === idRecognition.IdRecognitionCardType.IDCard && config.needAntiFraud) {
|
|
35916
|
+
// 先設定 stage 再 init,避免使用預設的 Frontal stage
|
|
35917
|
+
yield this.antiFraudInstance.setStage(antiFraudStageList);
|
|
35751
35918
|
yield this.antiFraudInstance.init();
|
|
35752
35919
|
} else if (config.type === idRecognition.IdRecognitionCardType.ResidentCard) {
|
|
35753
35920
|
// workaround: resident card need MRZ, refactor later.
|
|
@@ -35759,7 +35926,8 @@ class OCRModule {
|
|
|
35759
35926
|
yield util.waitTime(100);
|
|
35760
35927
|
return Object.assign(Object.assign({}, resp.parameters), {
|
|
35761
35928
|
expiredIn: resp.expiredIn,
|
|
35762
|
-
captureTimeout: config.captureTimeout
|
|
35929
|
+
captureTimeout: config.captureTimeout,
|
|
35930
|
+
ocrMaxFps: 2
|
|
35763
35931
|
});
|
|
35764
35932
|
}),
|
|
35765
35933
|
ocrStart: (points, type, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode, cardType, retry = false) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -35770,9 +35938,14 @@ class OCRModule {
|
|
|
35770
35938
|
_service instanceof idRecognition.CardOCR ? yield _service.setType(type) : null;
|
|
35771
35939
|
const newParams = {};
|
|
35772
35940
|
const oldParams = yield this.ocrService.getParams();
|
|
35773
|
-
|
|
35774
|
-
|
|
35775
|
-
newParams[
|
|
35941
|
+
// V9: 根據卡片類型設置參數 (參考 Android SDK)
|
|
35942
|
+
const cardParams = CARD_OCR_PARAMS_MAP[cardType] || DEFAULT_CARD_OCR_PARAMS;
|
|
35943
|
+
newParams[F_IMAGE_BLUR_TH] = cardParams.imageBlurTh;
|
|
35944
|
+
newParams[F_IDCARD_COLOR_TH] = cardParams.idCardColorTh;
|
|
35945
|
+
newParams[F_IMAGE_REFLECTIVE_TRIGGER_TH] = cardParams.imageReflectiveTriggerTh;
|
|
35946
|
+
newParams[F_IMAGE_REFLECTIVE_MASK_TH] = cardParams.imageReflectiveMaskTh;
|
|
35947
|
+
newParams[F_CARD_MATCH_TH] = cardParams.cardMatchTh;
|
|
35948
|
+
newParams[F_CARD_CLASSIFICATION_TH] = cardParams.cardClassificationTh;
|
|
35776
35949
|
yield this.ocrService.setParams(Object.assign(Object.assign({}, oldParams), newParams));
|
|
35777
35950
|
if (cardTypes.map(idRecognition.mapCardtypeToAuthmeClass).filter(n => n == engine.EAuthMeCardClass.Unknown).length > 0) {
|
|
35778
35951
|
this.ocrService.setOption({
|
|
@@ -35936,9 +36109,12 @@ class OCRModule {
|
|
|
35936
36109
|
const newParams = {};
|
|
35937
36110
|
const oldParams = yield _service.getParams();
|
|
35938
36111
|
if (config.antiFraudIMetalTagValidCountTh !== false) newParams['iMetalTagValidCountTh'] = config.antiFraudIMetalTagValidCountTh;
|
|
36112
|
+
// V9: 根據卡片類型設置參數 (參考 Android SDK)
|
|
36113
|
+
const cardParams = CARD_OCR_PARAMS_MAP[cardType] || DEFAULT_CARD_OCR_PARAMS;
|
|
35939
36114
|
newParams['timeoutSec'] = 1000;
|
|
35940
|
-
newParams[
|
|
35941
|
-
newParams[
|
|
36115
|
+
newParams[F_IDCARD_COLOR_TH] = cardParams.idCardColorTh;
|
|
36116
|
+
newParams[F_IMAGE_REFLECTIVE_TRIGGER_TH] = cardParams.imageReflectiveTriggerTh;
|
|
36117
|
+
newParams[F_CARD_MATCH_TH] = cardParams.cardMatchTh;
|
|
35942
36118
|
newParams['fImageThicknessTh'] = 0.4;
|
|
35943
36119
|
newParams['fCardDeformationTh'] = 10;
|
|
35944
36120
|
newParams['enableCardInROI'] = 1;
|
|
@@ -36278,8 +36454,16 @@ class OCRModule {
|
|
|
36278
36454
|
});
|
|
36279
36455
|
const ocrOriginImg = util.UintArrayToBlob(frameWidth, frameHeight, data, virtualCanvas, config.resultImageFormat);
|
|
36280
36456
|
const eClass = cardType !== null && cardType !== void 0 ? cardType : '';
|
|
36281
|
-
if (result.eStatus === idRecognition.EAuthMeCardOCRStatus.Pass &&
|
|
36282
|
-
|
|
36457
|
+
if (result.eStatus === idRecognition.EAuthMeCardOCRStatus.Pass && !!docInfos[eClass].docId) {
|
|
36458
|
+
// V9: 如果有卡片圖像就使用它,否則使用原始幀圖像
|
|
36459
|
+
let resultOcrImg;
|
|
36460
|
+
if (result.imageData && result.iWidth > 0 && result.iHeight > 0) {
|
|
36461
|
+
resultOcrImg = util.UintArrayToBlob(result.iWidth, result.iHeight, result.imageData, virtualCanvas, config.resultImageFormat);
|
|
36462
|
+
} else {
|
|
36463
|
+
// V9 沒有返回卡片圖像,使用原始幀圖像作為替代
|
|
36464
|
+
console.warn('[OCR] V9: no card image available, using original frame image');
|
|
36465
|
+
resultOcrImg = ocrOriginImg;
|
|
36466
|
+
}
|
|
36283
36467
|
docInfos[eClass].ocrImg = resultOcrImg;
|
|
36284
36468
|
docInfos[eClass].ocrOriginImg = ocrOriginImg;
|
|
36285
36469
|
yield _service.stop();
|
|
@@ -36365,6 +36549,12 @@ class OCRModule {
|
|
|
36365
36549
|
} else {
|
|
36366
36550
|
ocrOriginImg = docInfos[option.cardType].ocrOriginImg;
|
|
36367
36551
|
}
|
|
36552
|
+
// V9: 檢查 ocrOriginImg 是否有效
|
|
36553
|
+
if (!ocrOriginImg) {
|
|
36554
|
+
console.error('[OCR] confirmImage: ocrOriginImg is null or undefined, cannot proceed');
|
|
36555
|
+
console.error('[OCR] confirmImage: option.cardType =', option.cardType, 'docInfos[cardType] =', docInfos[option.cardType]);
|
|
36556
|
+
return false;
|
|
36557
|
+
}
|
|
36368
36558
|
// const base64Image = await blobToBase64(ocrOriginImg);
|
|
36369
36559
|
// console.log('confirmImage', base64Image);
|
|
36370
36560
|
const requestImg = yield encryptImageBase64(ocrOriginImg);
|
|
@@ -36491,6 +36681,8 @@ class OCRModule {
|
|
|
36491
36681
|
const item2 = res.fields.find(item => item.name === item1.name);
|
|
36492
36682
|
return item2 ? Object.assign(Object.assign({}, item1), item2) : item1;
|
|
36493
36683
|
}).concat(res.fields.filter(item2 => !ocrResultFilds.some(item1 => item1.name === item2.name)));
|
|
36684
|
+
// V9: 過濾掉重複/內部欄位
|
|
36685
|
+
ocrResultFilds = ocrResultFilds.filter(item => !OCR_FIELDS_TO_FILTER.includes(item.name));
|
|
36494
36686
|
delete docInfos[option.cardType];
|
|
36495
36687
|
return true;
|
|
36496
36688
|
} catch (error) {
|
|
@@ -36510,6 +36702,13 @@ class OCRModule {
|
|
|
36510
36702
|
} else {
|
|
36511
36703
|
ocrOriginImg = docInfos[cardType].ocrOriginImg;
|
|
36512
36704
|
}
|
|
36705
|
+
// V9: 檢查 ocrOriginImg 是否有效
|
|
36706
|
+
if (!ocrOriginImg) {
|
|
36707
|
+
console.warn('[OCR] ocrCancel: ocrOriginImg is null, skipping upload');
|
|
36708
|
+
yield this.ocrService.stop();
|
|
36709
|
+
docInfos[cardType].docId = '';
|
|
36710
|
+
return true;
|
|
36711
|
+
}
|
|
36513
36712
|
const requestImg = yield encryptImageBase64(ocrOriginImg);
|
|
36514
36713
|
yield this.ocrService.stop();
|
|
36515
36714
|
const report = yield this.ocrService.getReport();
|
|
@@ -36556,7 +36755,9 @@ class OCRModule {
|
|
|
36556
36755
|
}
|
|
36557
36756
|
}),
|
|
36558
36757
|
antiFraudStart: (points, setBorderType, setCardBorderColor, setBorderSuccess, scanAnimationContainer, successAnimationContainer, frameImage, frameText, faceMode) => __awaiter(this, void 0, void 0, function* () {
|
|
36559
|
-
|
|
36758
|
+
// setStage() 如果服務已初始化會自動觸發 reinit
|
|
36759
|
+
// 不需要再呼叫 init()
|
|
36760
|
+
yield this.antiFraudInstance.setStage(antiFraudStageList);
|
|
36560
36761
|
yield this.antiFraudInstance.setFrameSize(frameWidth, frameHeight);
|
|
36561
36762
|
yield this.antiFraudInstance.setMaskPosition(points.map(([x, y]) => [Number(x.toFixed(2)), Number(y.toFixed(2))]));
|
|
36562
36763
|
const newParams = {};
|
|
@@ -36564,7 +36765,6 @@ class OCRModule {
|
|
|
36564
36765
|
if (config.antiFraudIMetalTagValidCountTh !== false) newParams['iMetalTagValidCountTh'] = config.antiFraudIMetalTagValidCountTh;
|
|
36565
36766
|
newParams['timeoutSec'] = fraudTimeout;
|
|
36566
36767
|
yield this.antiFraudInstance.setParams(Object.assign(Object.assign({}, oldParams), newParams));
|
|
36567
|
-
yield this.antiFraudInstance.setStage(antiFraudStageList);
|
|
36568
36768
|
yield this.antiFraudInstance.startSession();
|
|
36569
36769
|
const twnidCardFront = engine.EAuthMeCardClass.TWN_IDCard_Front;
|
|
36570
36770
|
docInfos[twnidCardFront] = {
|
|
@@ -36825,8 +37025,10 @@ class OCRModule {
|
|
|
36825
37025
|
yield (_u = this.ocrService) === null || _u === void 0 ? void 0 : _u.destroy();
|
|
36826
37026
|
yield (_v = this.antiFraudInstance) === null || _v === void 0 ? void 0 : _v.destroy();
|
|
36827
37027
|
}),
|
|
36828
|
-
getCardMatchROI:
|
|
36829
|
-
return yield this.antiFraudInstance.getCardMatchROI(
|
|
37028
|
+
getCardMatchROI: stage => __awaiter(this, void 0, void 0, function* () {
|
|
37029
|
+
return yield this.antiFraudInstance.getCardMatchROI(stage ? {
|
|
37030
|
+
stage
|
|
37031
|
+
} : undefined);
|
|
36830
37032
|
})
|
|
36831
37033
|
}), eventNameWrong$.pipe(rxjs.tap(() => {
|
|
36832
37034
|
throw new core.AuthmeError(core.ErrorCode.EVENT_NAME_WRONG);
|
|
@@ -37343,8 +37545,8 @@ class AuthmeIdentityVerification extends engine.AuthmeFunctionModule {
|
|
|
37343
37545
|
}
|
|
37344
37546
|
|
|
37345
37547
|
var name = "authme/sdk";
|
|
37346
|
-
var version$1 = "2.8.
|
|
37347
|
-
var date = "2026-
|
|
37548
|
+
var version$1 = "2.8.42";
|
|
37549
|
+
var date = "2026-02-03T09:36:05+0000";
|
|
37348
37550
|
var packageInfo = {
|
|
37349
37551
|
name: name,
|
|
37350
37552
|
version: version$1,
|