209 lines
9.5 KiB
JavaScript
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);
|
|
});
|