| 功能 | 说明 |
|---|---|
| 设置时间 | 5 分钟 |
| 需要后端 | ✅ 是(用于创建会话) |
| 保留在您的域名上 | ❌ 否(会跳转到 verify.didit.me 或您的白标自定义域名) |
| 支持跨设备 | ✅ 是 |
| 浏览器兼容性 | ✅ 所有浏览器 |
何时使用重定向方式
- 跨设备验证:在桌面端开始,在移动端完成
- 最大兼容性:适用于所有浏览器,包括较旧的浏览器
- 集成简单:无需配置 iframe
- 摄像头问题:当 iframe 的摄像头访问存在问题时
实现
步骤 1 在后端创建会话
会话创建响应中有一个名为url 的字段,请将该值传递给浏览器。
// Node.js / Express
app.post('/api/create-verification', async (req, res) => {
const { userId } = req.body;
const response = await fetch('https://verification.didit.me/v3/session/', {
method: 'POST',
headers: {
'x-api-key': process.env.DIDIT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
workflow_id: process.env.DIDIT_WORKFLOW_ID,
vendor_data: userId,
callback: 'https://yourapp.com/verification-complete',
callback_method: 'both', // most reliable across desktop ↔ mobile handoffs
}),
});
if (!response.ok) {
return res.status(response.status).send(await response.text());
}
const session = await response.json();
// 201 per OpenAPI: { session_id, session_number, session_token, url, vendor_data,
// metadata, status, workflow_id, workflow_version, callback }
res.json({ sessionId: session.session_id, verificationUrl: session.url });
});
步骤 2 在前端重定向用户
HTML:<button onclick="startVerification()">Verify Identity</button>
<script>
async function startVerification() {
const response = await fetch('/api/create-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: 'user-123' })
});
const { verificationUrl } = await response.json();
window.location.href = verificationUrl;
}
</script>
function VerifyButton() {
const [loading, setLoading] = useState(false);
const startVerification = async () => {
setLoading(true);
const response = await fetch('/api/create-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: 'user-123' })
});
const { verificationUrl } = await response.json();
window.location.assign(verificationUrl);
};
return (
<button onClick={startVerification} disabled={loading}>
{loading ? 'Loading...' : 'Verify Identity'}
</button>
);
}
<template>
<button @click="startVerification" :disabled="loading">
{{ loading ? 'Loading...' : 'Verify Identity' }}
</button>
</template>
<script setup>
import { ref } from 'vue';
const loading = ref(false);
async function startVerification() {
loading.value = true;
const response = await fetch('/api/create-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: 'user-123' })
});
const { verificationUrl } = await response.json();
window.location.href = verificationUrl;
}
</script>
处理回调
验证完成后,Didit 会携带查询参数将用户重定向到您的回调 URL:https://yourapp.com/verification-complete?verificationSessionId=abc123&status=Approved
| 参数 | 说明 |
|---|---|
verificationSessionId | 唯一的会话标识符 |
status | 会话的当前状态,例如 Approved、Declined、In Review 或 Expired。参见验证状态 |
Next.js 示例
// pages/verification-complete.tsx
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
export default function VerificationComplete() {
const router = useRouter();
const { verificationSessionId, status } = router.query;
const [message, setMessage] = useState('Processing...');
useEffect(() => {
if (!router.isReady) return;
async function handleResult() {
// Optionally verify the result with your backend
await fetch('/api/verify-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId: verificationSessionId,
status
})
});
// Redirect based on status
switch (status) {
case 'Approved':
setMessage('Verification successful! Redirecting...');
setTimeout(() => router.push('/dashboard'), 2000);
break;
case 'Declined':
setMessage('Verification was not successful.');
setTimeout(() => router.push('/verification-failed'), 2000);
break;
case 'In Review':
setMessage('Your verification is under review.');
setTimeout(() => router.push('/verification-pending'), 2000);
break;
default:
setMessage('Unknown status. Please contact support.');
}
}
handleResult();
}, [router.isReady, verificationSessionId, status]);
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="animate-spin w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full mb-4" />
<p className="text-lg">{message}</p>
</div>
);
}
Express.js 示例
// routes/verification.js
app.get('/verification-complete', async (req, res) => {
const { verificationSessionId, status } = req.query;
// Optionally verify with Didit API
// const session = await diditApi.getSession(verificationSessionId);
// Update your database
await db.users.update({
where: { verificationSessionId },
data: { verificationStatus: status }
});
// Redirect based on status
switch (status) {
case 'Approved':
res.redirect('/dashboard?verified=true');
break;
case 'Declined':
res.redirect('/verification-failed');
break;
case 'In Review':
res.redirect('/verification-pending');
break;
default:
res.redirect('/error');
}
});
Vue Router 示例
// router/index.ts
{
path: '/verification-complete',
component: () => import('@/views/VerificationComplete.vue'),
beforeEnter: async (to, from, next) => {
const { verificationSessionId, status } = to.query;
// Store result and redirect
await store.dispatch('verification/handleResult', {
sessionId: verificationSessionId,
status
});
if (status === 'Approved') {
next('/dashboard');
} else if (status === 'Declined') {
next('/verification-failed');
} else {
next('/verification-pending');
}
}
}
在新标签页中打开
如果您希望在用户验证时保持您的应用处于打开状态:// Open in new tab
window.open(verificationUrl, '_blank');
// Poll for completion or use webhooks
使用新标签页时,请结合 Webhook 来了解验证何时完成。
安全最佳实践
在服务器端验证回调
请将verificationSessionId 和 status 查询参数视为不可信的界面提示。始终在您的后端确认最终决策,方式是处理 Webhook(推荐),或通过 API 重新获取决策结果:
// Backend: re-fetch the canonical decision before granting access
app.get('/verification-complete', async (req, res) => {
const { verificationSessionId } = req.query;
const response = await fetch(
`https://verification.didit.me/v3/session/${verificationSessionId}/decision/`,
{
headers: { 'x-api-key': process.env.DIDIT_API_KEY },
}
);
if (!response.ok) {
return res.status(response.status).send(await response.text());
}
const decision = await response.json();
// decision.status uses the canonical enum:
// Not Started | In Progress | Awaiting User | In Review | Approved |
// Declined | Resubmitted | Expired | Kyc Expired | Abandoned
if (decision.status === 'Approved') {
// Grant access. Per-feature reports are plural arrays:
// decision.id_verifications[], decision.face_matches[], decision.aml_screenings[], ...
}
res.redirect('/dashboard');
});
使用 Webhook
为获得最可靠的集成,请使用 Webhook 将验证结果直接发送到您的后端,无论用户浏览器的行为如何。跨设备流程
重定向方式天然支持跨设备验证:- 用户在桌面端点击“验证”
- 用户被重定向到验证页面
- 如有需要,用户可以扫描二维码,在移动端继续操作
- 完成后,用户会被重定向到您的回调 URL,由
callback_method(可取值为initiator、completer或both)决定哪台设备进行重定向
故障排查
未收到回调
- 确认在创建会话时已正确设置回调 URL
- 确保您的回调 URL 可以从互联网访问
- 检查该 URL 是否存在拼写错误或编码问题
状态始终显示“已拒绝”
- 在 Didit 控制台中检查您的工作流设置
- 确认您使用的测试证件有效
- 通过 API 查看会话详情,获取具体的拒绝原因
重定向循环
- 确保您的回调处理程序不会重定向回验证页面
- 检查回调页面是否存在缓存问题
- 清除浏览器缓存后重试
示例代码库
GitHub 代码库
在 GitHub 上查看源代码和示例