最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - Embed Stripe Identity Verification instead of opening a lightbox with Stripe's designflow - Stack Overflow

programmeradmin1浏览0评论

I’m working on a Next.js (15.1.6) app with React (19.0.0) and trying to integrate Stripe Identity Verification fully embedded within my UI—without redirecting to Stripe’s hosted page or opening a lightbox/modal via stripe.verifyIdentity. My goal is to capture photos (front ID, back ID, selfie) in my custom UI and submit them to Stripe seamlessly.

What I’ve Tried

Initially, I used [email protected] and uploaded files client-side via with purpose: 'identity_document'. After creating a VerificationSession server-side, I called stripe.verifyIdentity(clientSecret) from @stripe/stripe-js, but it opens a modal that restarts the photo capture process, ignoring my uploads.

I downgraded to [email protected] (and tested 10.17.0) hoping to use a server-side submit method to finalize the session with my uploaded files, but submit isn’t available in VerificationSessionsResource (only create, retrieve, update, list, cancel, redact). I also tried update with a hypothetical provided_documents param to link file IDs, but it’s not a valid property in VerificationSessionUpdateParams.

Here’s a simplified version of my current code with 16.12.0:

// src/lib/stripe.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-08-01' });

// src/app/api/identity/create-verification-session/route.ts
import { NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';
export async function POST(req: Request) {
  const { userId } = await req.json();
  const session = await stripe.identity.verificationSessions.create({
    type: 'document',
    options: { document: { require_matching_selfie: true } },
    metadata: { user_id: userId },
  });
  return NextResponse.json({ id: session.id, client_secret: session.client_secret });
}

// src/components/IdentityVerification.tsx
'use client';
import { useState, useRef, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

export default function IdentityVerification({ userId }: { userId: string }) {
  const [session, setSession] = useState<{ id: string; client_secret: string } | null>(null);
  const [step, setStep] = useState<'id_front' | 'id_back' | 'selfie'>('id_front');
  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    fetch('/api/identity/create-verification-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId }),
    })
      .then((res) => res.json())
      .then(setSession);
  }, [userId]);

  const capturePhoto = async () => {
    const context = canvasRef.current?.getContext('2d');
    if (context && videoRef.current) {
      context.drawImage(videoRef.current, 0, 0, 300, 225);
      canvasRef.current?.toBlob(async (blob) => {
        if (!blob) return;
        const formData = new FormData();
        formData.append('file', blob, `${step}.jpg`);
        formData.append('purpose', 'identity_document');
        
        const res = await fetch('', {
          method: 'POST',
          headers: { Authorization: `Bearer ${process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}` },
          body: formData,
        });
        const data = await res.json();

        if (step === 'id_front') setStep('id_back');
        else if (step === 'id_back') setStep('selfie');
        else if (step === 'selfie') submitVerification();
      }, 'image/jpeg');
    }
  };

  const submitVerification = async () => {
    if (!session) return;
    const stripe = await stripePromise;
    await stripe?.verifyIdentity(session.client_secret); // Triggers modal
  };

  return (
    <div>
      <video ref={videoRef} autoPlay />
      <canvas ref={canvasRef} width="300" height="225" className="hidden" />
      <button onClick={capturePhoto}>Capture Photo</button>
    </div>
  );
}

The Problem

  • verifyIdentity opens a modal, duplicating the photo capture process.

  • No server-side submit or way to link files to the session without the modal.

  • The Stripe docs () suggest create and verifyIdentity, but nothing about embedding without a lightbox.

Questions

  1. Is it possible to embed Stripe Identity Verification fully in my Next.js app without a modal/lightbox, using my own photo capture UI?

  2. If submit was removed, is there an alternative server-side method in 16.12.0 (or any version) to finalize a session with custom-uploaded files?

  3. Has anyone successfully bypassed verifyIdentity’s modal while still verifying files?

Any insights, workarounds, or confirmation that this isn’t possible would be greatly appreciated! I’d rather not downgrade further unless necessary, but I’m open to suggestions.

I’m working on a Next.js (15.1.6) app with React (19.0.0) and trying to integrate Stripe Identity Verification fully embedded within my UI—without redirecting to Stripe’s hosted page or opening a lightbox/modal via stripe.verifyIdentity. My goal is to capture photos (front ID, back ID, selfie) in my custom UI and submit them to Stripe seamlessly.

What I’ve Tried

Initially, I used [email protected] and uploaded files client-side via https://files.stripe/v1/files with purpose: 'identity_document'. After creating a VerificationSession server-side, I called stripe.verifyIdentity(clientSecret) from @stripe/stripe-js, but it opens a modal that restarts the photo capture process, ignoring my uploads.

I downgraded to [email protected] (and tested 10.17.0) hoping to use a server-side submit method to finalize the session with my uploaded files, but submit isn’t available in VerificationSessionsResource (only create, retrieve, update, list, cancel, redact). I also tried update with a hypothetical provided_documents param to link file IDs, but it’s not a valid property in VerificationSessionUpdateParams.

Here’s a simplified version of my current code with 16.12.0:

// src/lib/stripe.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-08-01' });

// src/app/api/identity/create-verification-session/route.ts
import { NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';
export async function POST(req: Request) {
  const { userId } = await req.json();
  const session = await stripe.identity.verificationSessions.create({
    type: 'document',
    options: { document: { require_matching_selfie: true } },
    metadata: { user_id: userId },
  });
  return NextResponse.json({ id: session.id, client_secret: session.client_secret });
}

// src/components/IdentityVerification.tsx
'use client';
import { useState, useRef, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

export default function IdentityVerification({ userId }: { userId: string }) {
  const [session, setSession] = useState<{ id: string; client_secret: string } | null>(null);
  const [step, setStep] = useState<'id_front' | 'id_back' | 'selfie'>('id_front');
  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    fetch('/api/identity/create-verification-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId }),
    })
      .then((res) => res.json())
      .then(setSession);
  }, [userId]);

  const capturePhoto = async () => {
    const context = canvasRef.current?.getContext('2d');
    if (context && videoRef.current) {
      context.drawImage(videoRef.current, 0, 0, 300, 225);
      canvasRef.current?.toBlob(async (blob) => {
        if (!blob) return;
        const formData = new FormData();
        formData.append('file', blob, `${step}.jpg`);
        formData.append('purpose', 'identity_document');
        
        const res = await fetch('https://files.stripe/v1/files', {
          method: 'POST',
          headers: { Authorization: `Bearer ${process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}` },
          body: formData,
        });
        const data = await res.json();

        if (step === 'id_front') setStep('id_back');
        else if (step === 'id_back') setStep('selfie');
        else if (step === 'selfie') submitVerification();
      }, 'image/jpeg');
    }
  };

  const submitVerification = async () => {
    if (!session) return;
    const stripe = await stripePromise;
    await stripe?.verifyIdentity(session.client_secret); // Triggers modal
  };

  return (
    <div>
      <video ref={videoRef} autoPlay />
      <canvas ref={canvasRef} width="300" height="225" className="hidden" />
      <button onClick={capturePhoto}>Capture Photo</button>
    </div>
  );
}

The Problem

  • verifyIdentity opens a modal, duplicating the photo capture process.

  • No server-side submit or way to link files to the session without the modal.

  • The Stripe docs (https://docs.stripe/identity/verification-sessions) suggest create and verifyIdentity, but nothing about embedding without a lightbox.

Questions

  1. Is it possible to embed Stripe Identity Verification fully in my Next.js app without a modal/lightbox, using my own photo capture UI?

  2. If submit was removed, is there an alternative server-side method in 16.12.0 (or any version) to finalize a session with custom-uploaded files?

  3. Has anyone successfully bypassed verifyIdentity’s modal while still verifying files?

Any insights, workarounds, or confirmation that this isn’t possible would be greatly appreciated! I’d rather not downgrade further unless necessary, but I’m open to suggestions.

Share Improve this question asked Feb 21 at 3:52 William HartmanWilliam Hartman 431 silver badge3 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Stripe Identity does not allow you to use your own photo capture UI, or pass in your own files via the API to be verified.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论