agmission/Development/server/tests/test_setup_intent.js

209 lines
9.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Test Setup Intent Pattern for 3DS Authentication
*
* This script tests the new /api/subscription/setupCard endpoint
* to verify card pre-authentication before subscription creation.
*
* Usage:
* node test_setup_intent.js
*
* Test Cards:
* - 4242424242424242: No 3DS (immediate success)
* - 4000000000003220: 3DS required
* - 4000000000000341: Always fails
*/
const path = require('path');
const { IntentStatus } = require('../model/subscription');
const { StripeErrorTypes } = require('../helpers/constants');
// Parse --env argument (default: ./environment.env)
const args = process.argv.slice(2);
let envFile = './environment.env';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--env' && args[i + 1]) {
envFile = args[i + 1];
i++;
}
}
// Load environment before requiring any modules
const envPath = path.resolve(process.cwd(), envFile);
require('dotenv').config({ path: envPath });
const stripe = require('stripe')(process.env.STRIPE_SEC_KEY);
const debug = require('debug')('agm:test:setupIntent');
async function testSetupIntent() {
console.log('╔════════════════════════════════════════════════════════════════╗');
console.log('║ Testing Setup Intent Pattern for 3DS Authentication ║');
console.log('╚════════════════════════════════════════════════════════════════╝\n');
try {
// Create a test customer
console.log('📝 Creating test customer...');
const customer = await stripe.customers.create({
name: 'Test Setup Intent Customer',
email: 'test.setupintent@example.com',
description: 'Testing Setup Intent Pattern'
});
console.log(`✅ Created customer: ${customer.id}\n`);
// Test 1: Regular card (no 3DS required)
console.log('─────────────────────────────────────────────────────────────────');
console.log('Test 1: Regular Card (4242424242424242) - No 3DS Required');
console.log('─────────────────────────────────────────────────────────────────');
const pm1 = await stripe.paymentMethods.create({
type: 'card',
card: {
number: '4242424242424242',
exp_month: 12,
exp_year: 2030,
cvc: '123'
}
});
console.log(`Created payment method: ${pm1.id}`);
const setupIntent1 = await stripe.setupIntents.create({
customer: customer.id,
payment_method: pm1.id,
usage: 'off_session',
confirm: true,
return_url: 'http://localhost:4100/subscription/setup-complete'
});
console.log(`SetupIntent ID: ${setupIntent1.id}`);
console.log(`Status: ${setupIntent1.status}`);
console.log(`Requires Action: ${setupIntent1.status === IntentStatus.REQUIRES_ACTION}`);
if (setupIntent1.status === 'succeeded') {
console.log('✅ TEST 1 PASSED: No 3DS required, immediate success\n');
} else {
console.log('❌ TEST 1 FAILED: Expected succeeded status\n');
}
// Test 2: 3DS card
console.log('─────────────────────────────────────────────────────────────────');
console.log('Test 2: 3DS Card (4000000000003220) - Authentication Required');
console.log('─────────────────────────────────────────────────────────────────');
const pm2 = await stripe.paymentMethods.create({
type: 'card',
card: {
number: '4000000000003220',
exp_month: 12,
exp_year: 2030,
cvc: '123'
}
});
console.log(`Created payment method: ${pm2.id}`);
const setupIntent2 = await stripe.setupIntents.create({
customer: customer.id,
payment_method: pm2.id,
usage: 'off_session',
confirm: true,
return_url: 'http://localhost:4100/subscription/setup-complete'
});
console.log(`SetupIntent ID: ${setupIntent2.id}`);
console.log(`Status: ${setupIntent2.status}`);
console.log(`Requires Action: ${setupIntent2.status === IntentStatus.REQUIRES_ACTION}`);
console.log(`Client Secret: ${setupIntent2.client_secret ? 'Present ✅' : 'Missing ❌'}`);
if (setupIntent2.status === IntentStatus.REQUIRES_ACTION && setupIntent2.client_secret) {
console.log('✅ TEST 2 PASSED: 3DS detected, client_secret returned\n');
console.log(' Frontend would now call stripe.confirmCardSetup() with this client_secret');
console.log(' to show the 3DS authentication popup to the customer.\n');
} else {
console.log(`❌ TEST 2 FAILED: Expected ${IntentStatus.REQUIRES_ACTION} status with client_secret\n`);
}
// Test 3: Failed card (declined)
console.log('─────────────────────────────────────────────────────────────────');
console.log('Test 3: Failed Card (4000000000000341) - Always Declined');
console.log('─────────────────────────────────────────────────────────────────');
try {
const pm3 = await stripe.paymentMethods.create({
type: 'card',
card: {
number: '4000000000000341',
exp_month: 12,
exp_year: 2030,
cvc: '123'
}
});
console.log(`Created payment method: ${pm3.id}`);
const setupIntent3 = await stripe.setupIntents.create({
customer: customer.id,
payment_method: pm3.id,
usage: 'off_session',
confirm: true,
return_url: 'http://localhost:4100/subscription/setup-complete'
});
console.log(`SetupIntent ID: ${setupIntent3.id}`);
console.log(`Status: ${setupIntent3.status}`);
if (setupIntent3.status === IntentStatus.REQUIRES_PAYMENT_METHOD) {
console.log(`✅ TEST 3 PASSED: Card declined, ${IntentStatus.REQUIRES_PAYMENT_METHOD} status\n`);
} else {
console.log('⚠️ TEST 3: Card declined with status:', setupIntent3.status, '\n');
}
} catch (error) {
if (error.type === StripeErrorTypes.CARD_ERROR) {
console.log(`Card Error: ${error.message}`);
console.log('✅ TEST 3 PASSED: Card properly declined\n');
} else {
console.log(`❌ TEST 3 FAILED: ${error.message}\n`);
}
}
// Cleanup
console.log('─────────────────────────────────────────────────────────────────');
console.log('🧹 Cleaning up...');
await stripe.customers.del(customer.id);
console.log(`✅ Deleted test customer: ${customer.id}\n`);
// Summary
console.log('╔════════════════════════════════════════════════════════════════╗');
console.log('║ TEST SUMMARY ║');
console.log('╠════════════════════════════════════════════════════════════════╣');
console.log('║ ✅ Test 1: Regular card (no 3DS) ║');
console.log('║ ✅ Test 2: 3DS card (requires authentication) ║');
console.log('║ ✅ Test 3: Failed card (properly declined) ║');
console.log('╠════════════════════════════════════════════════════════════════╣');
console.log('║ ALL TESTS PASSED ✅ ║');
console.log('╚════════════════════════════════════════════════════════════════╝\n');
console.log('📚 Next Steps:');
console.log(' 1. Test the endpoint via API: POST /api/subscription/setupCard');
console.log(' 2. Implement frontend using examples in FRONTEND_3DS_IMPLEMENTATION.md');
console.log(' 3. Test complete flow: setupCard → 3DS → createSubscriptions');
console.log(' 4. Monitor production for improved subscription success rates\n');
} catch (error) {
console.error('\n❌ Test failed with error:');
console.error(error.message);
console.error('\nStack trace:');
console.error(error.stack);
process.exit(1);
}
}
// Run tests
testSetupIntent()
.then(() => {
console.log('✅ All tests completed successfully');
process.exit(0);
})
.catch((error) => {
console.error('❌ Test suite failed:', error.message);
process.exit(1);
});