Migration Guide: Payment Element → Corgi-Controlled Confirmation
Add Corgi Model fraud decisioning to your existing Stripe Payment Element integration with one backend change and no checkout UX impact.
On this page
If you process payments using Stripe Payment Element with PaymentIntent, you can start using Corgi Model or Corgi Rule Engine by migrating to Corgi-Controlled PaymentIntent Confirmation. This enables deterministic Request 3DS, Allow, Block, and Review decisions enforced by Corgi Labs before payment confirmation, without changing your checkout UX.
Changes at a high level
You will make one flow change. Instead of having Stripe's standard client confirmation flow finalize the payment, you collect payment details in your existing Payment Element, create a Stripe ConfirmationToken, send it to your backend, and let your server call Corgi Labs' confirm-payment endpoint.
Your checkout UX remains largely unchanged, but the confirmation control is moved to your server. Your customers should not experience any noticeable changes.
| Before | After | |
|---|---|---|
| Client flow | Mount Payment Element, then call stripe.confirmPayment(...) | Mount Payment Element, collect a Fingerprint event, call elements.submit(), create a ConfirmationToken, then call your backend finalize endpoint with confirmationTokenId and fpEventId |
| Server flow | Create PaymentIntent directly with Stripe | Call Corgi Labs to create the PaymentIntent, evaluate rules, and execute confirmation |
| Risk evaluation | Controlled by Stripe Radar | Controlled by Corgi Model and Corgi Rule Engine |
Migrate to Corgi-Controlled Confirmation
Step 1 — Replace direct PaymentIntent creation with server finalization
Allow Corgi Labs to evaluate the payment during the server-finalize flow. This is the main backend change.
❌ Before (direct Stripe call)
# server.py
pi = stripe.PaymentIntent.create(
amount=5000,
currency="usd",
automatic_payment_methods={"enabled": True},
metadata={"order_id": "123"},
)
return {"client_secret": pi.client_secret}
✅ After (HTTP API example, recommended)
Create a CORGI_API_TOKEN in your Corgi Web App at app.corgilabs.ai. Keep that token on your server only.
# server.py
import os
import requests
CORGI_API_TOKEN = os.environ["CORGI_API_TOKEN"]
# /api/finalize-payment
def finalize_payment(confirmation_token_id, fp_event_id):
response = requests.post(
"https://app.corgilabs.ai/api/v1/corgi-sdk/confirm-payment",
headers={
"Authorization": f"Bearer {CORGI_API_TOKEN}",
"Content-Type": "application/json",
},
json={
"confirmationTokenId": confirmation_token_id,
"paymentIntentParams": {
"amount": 5000,
"currency": "usd",
"metadata": {
"order_id": "123",
"fp_event_id": fp_event_id,
}
}
},
timeout=15,
)
response.raise_for_status()
return response.json()
Optional: SDK (Node.js) example
import { CorgiSDK } from 'corgi-sdk';
const corgi = new CorgiSDK({
token: process.env.CORGI_API_TOKEN,
});
// /api/finalize-payment
export async function finalizePayment(
confirmationTokenId: string,
fpEventId: string,
) {
return await corgi.confirmPayment(confirmationTokenId, {
amount: 5000,
currency: 'usd',
metadata: {
order_id: '<order_id>',
fp_event_id: fpEventId,
},
});
}
Step 2 — Change the frontend to create a Stripe ConfirmationToken
Move the client from Stripe-controlled confirmation to token creation plus server finalization. The checkout UI remains the same, but the submit path changes.
Your frontend should also embed Fingerprint.js, collect a Fingerprint event ID for each checkout attempt, and send that fpEventId to your backend. Your backend should persist that value to PaymentIntent metadata as fp_event_id during the Corgi confirm call. This lets Corgi retrieve IP address, user agent, and related request/device context.
❌ Before (client confirms directly with Stripe)
const stripe = Stripe(PUBLISHABLE_KEY);
const elements = stripe.elements({
mode: "payment",
currency: "usd",
amount: 5000,
captureMethod: 'manual',
paymentMethodCreation: 'manual',
});
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: "<your return url>",
},
});
if (error) {
window.alert(error.message);
}
✅ After (client creates ConfirmationToken, server finalizes)
import * as Fingerprint from '@fingerprint/agent';
const stripe = Stripe(PUBLISHABLE_KEY);
const elements = stripe.elements({
mode: 'payment',
currency: 'usd',
amount: 5000,
});
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
const submitResult = await elements.submit();
if (submitResult.error) {
window.alert(submitResult.error.message);
return;
}
const tokenResult = await stripe.createConfirmationToken({
elements,
params: {
payment_method_data: {
billing_details: {
name: '<name>',
email: '<email>',
},
},
return_url: '<your return url>',
},
});
if (tokenResult.error) {
window.alert(tokenResult.error.message);
return;
}
const fp = Fingerprint.start({
apiKey: 'CXTnbDDa5sn49p2NSHNm',
endpoints: "https://metrics.corgilabs.ai"
});
const fpResult = await fp.get();
const fpEventId = fpResult.event_id;
const result = await fetch('/api/finalize-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
confirmationTokenId: tokenResult.confirmationToken.id,
fpEventId,
}),
}).then((res) => res.json());
if (result.action === 'block') {
window.alert('Your card was declined.');
return;
}
if (result.action === 'request_3ds' || result.status === 'requires_action') {
const { error } = await stripe.handleNextAction({
clientSecret: result.clientSecret,
});
if (error) {
window.alert('We are unable to authenticate your payment method. Please choose a different payment method and try again');
return;
}
}
Expo / React Native Fingerprint event ID
For mobile checkout, keep the backend payload unchanged. The React Native SDK returns the per-identification event ID as requestId; send that value as fpEventId.
pnpm add @fingerprintjs/fingerprintjs-pro-react-native
For Expo, add the native config plugin and use a native build or custom dev client. Expo Go is not supported because the SDK uses native code.
{
"expo": {
"plugins": ["@fingerprintjs/fingerprintjs-pro-react-native"]
}
}
Wrap the app with the Fingerprint provider using Corgi's public API key and metrics endpoint. In the React Native SDK, this endpoint config is endpointUrl.
import { FingerprintJsProProvider } from '@fingerprintjs/fingerprintjs-pro-react-native';
export function AppRoot() {
return (
<FingerprintJsProProvider
apiKey="CXTnbDDa5sn49p2NSHNm"
endpointUrl="https://metrics.corgilabs.ai"
>
<App />
</FingerprintJsProProvider>
);
}
In the mobile checkout submit path, collect requestId and pass it to the same finalize endpoint used by web:
import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react-native';
const { getData } = useVisitorData();
const visitorData = await getData();
const fpEventId = visitorData.requestId;
await fetch('/api/finalize-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
confirmationTokenId,
fpEventId,
}),
});
No backend change is needed. Continue saving the value as paymentIntentParams.metadata.fp_event_id during the Corgi confirm call.
See Fingerprint's React Native SDK docs and package README for native setup details.
Step 3 — Optional: Add metadata for better decisions
In addition to the required fp_event_id field used for device and request context, you can pass any metadata you already have. For example:
{
"metadata": {
"user_id": "u_456",
"loyalty_tier": "gold"
}
}
Corgi Labs automatically ingests the custom metadata. These fields can be used for Corgi Model evaluation and Rule Management immediately.
Step 4 — Optional: Subscribe to webhooks
We recommend subscribing to the following events:
payment_intent.succeededpayment_intent.payment_failed
If you already listen to Stripe PaymentIntent events, no changes are required.
Handle payments with Corgi-Controlled Confirmation
For each checkout attempt routed to Corgi after the migration:
The client mounts Payment Element and initializes Fingerprint.js.
The client collects
fpEventIdand callselements.submit().The client creates a Stripe
ConfirmationToken.The client sends
confirmationTokenIdandfpEventIdto your backend.Your backend calls Corgi's confirm-payment endpoint or SDK, creates the PaymentIntent, and saves
fp_event_idin metadata.Corgi Labs creates the draft
PaymentIntent, evaluates rules, and executes the confirmation flow.Corgi Labs returns a result such as
allow,block,request_3ds, orreview, along with Stripe status fields.If Stripe requires customer authentication, the client calls
stripe.handleNextAction(...)using the returnedclientSecret.
API response shape
confirmPayment(confirmationTokenId, paymentIntentParams) returns:
action:allow,block,none,request_3ds, orreviewmatchedRuleId: the matched rule ID, if anyrequest3ds: whether Corgi forced 3DS on the underlying PaymentIntent confirmationpaymentIntentId: the Stripe PaymentIntent ID created by Corgistatus: the latest Stripe PaymentIntent statusclientSecret: the Stripe client secret when Stripe returns it
Pass the intended Stripe PaymentIntentCreateParams as the second argument when using the SDK. Corgi uses those parameters to create the draft PaymentIntent before rule evaluation and confirmation.
Last updated: 2026-07-08