⚡ 3DS frictionless
3D Secure authentication that completes silently — no challenge popup. The auth happens in a hidden iframe, takes ~3-6 seconds, and you go straight to the success screen.
What you're testing. That 3DS data (cavv / eci / xid) is forwarded to NMI, that the auth flow doesn't visibly interrupt the customer, and that
payment.succeeded still fires correctly.What to do
- Click Run scenario.
- On the hosted page, enter
4000 0000 0000 2701, any future expiry, any CVV. - Click Pay. The button might look stuck for a few seconds — that's the silent 3DS auth running in the background. You should NOT see an OTP popup.
- You'll land on the success screen as if 3DS wasn't there at all.
payment.succeededwebhook arrives — check the envelope on the Event log for the auth fields.
The code (same as Standard)
No client-side change needed — the merchant's nmi3dsEnabled flag toggles 3DS on the hosted page. From the API side, the request body is identical.
// Node.js
const r = await fetch('https://app.sknpay.com/api/v1/payments', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.SKNPAY_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'order-' + Date.now(),
},
body: JSON.stringify({
amount: 1500, // minor units — 15.00 USD
currency: 'USD',
description: '3DS frictionless test',
customer_email: 'test@example.com',
success_url: 'https://yoursite.com/thanks',
cancel_url: 'https://yoursite.com/cart',
}),
})
const payment = await r.json()
res.redirect(303, payment.url) // → customer pays on hosted page
<?php
$ch = curl_init('https://app.sknpay.com/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SKNPAY_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: order-' . time(),
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 1500,
'currency' => 'USD',
'description' => '3DS frictionless test',
'customer_email' => 'test@example.com',
'success_url' => 'https://yoursite.com/thanks',
'cancel_url' => 'https://yoursite.com/cart',
]),
]);
$payment = json_decode(curl_exec($ch), true);
header('Location: ' . $payment['url'], true, 303);
# Python (Flask + requests)
r = requests.post(
'https://app.sknpay.com/api/v1/payments',
headers={
'Authorization': f'Bearer {os.environ["SKNPAY_API_KEY"]}',
'Content-Type': 'application/json',
'Idempotency-Key': f'order-{int(time.time())}',
},
json={
'amount': 1500,
'currency': 'USD',
'description': '3DS frictionless test',
'customer_email': 'test@example.com',
'success_url': 'https://yoursite.com/thanks',
'cancel_url': 'https://yoursite.com/cart',
},
)
return redirect(r.json()['url'], code=303)
curl -X POST https://app.sknpay.com/api/v1/payments \
-H "Authorization: Bearer $SKNPAY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-$(date +%s)" \
-d '{
"amount": 1500,
"currency": "USD",
"description": "3DS frictionless test",
"customer_email": "test@example.com",
"success_url": "https://yoursite.com/thanks",
"cancel_url": "https://yoursite.com/cart"
}'