init
This commit is contained in:
237
.opencode/skills/payment-integration/scripts/test-scripts.js
Executable file
237
.opencode/skills/payment-integration/scripts/test-scripts.js
Executable file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test suite for payment integration scripts
|
||||
*/
|
||||
|
||||
const SePayWebhookVerifier = require('./sepay-webhook-verify');
|
||||
const PolarWebhookVerifier = require('./polar-webhook-verify');
|
||||
const CheckoutHelper = require('./checkout-helper');
|
||||
|
||||
class TestRunner {
|
||||
constructor() {
|
||||
this.passed = 0;
|
||||
this.failed = 0;
|
||||
}
|
||||
|
||||
test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(`✓ ${name}`);
|
||||
this.passed++;
|
||||
} catch (error) {
|
||||
console.error(`✗ ${name}`);
|
||||
console.error(` Error: ${error.message}`);
|
||||
this.failed++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message || 'Assertion failed');
|
||||
}
|
||||
}
|
||||
|
||||
assertEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(message || `Expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
summary() {
|
||||
console.log(`\nTest Summary: ${this.passed} passed, ${this.failed} failed`);
|
||||
return this.failed === 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests
|
||||
console.log('Running Payment Integration Script Tests\n');
|
||||
const runner = new TestRunner();
|
||||
|
||||
// SePay Webhook Verifier Tests
|
||||
console.log('SePay Webhook Verifier Tests:');
|
||||
|
||||
runner.test('should verify valid SePay webhook', () => {
|
||||
const verifier = new SePayWebhookVerifier('none');
|
||||
const payload = {
|
||||
id: 12345,
|
||||
gateway: 'Vietcombank',
|
||||
transactionDate: '2025-01-13 10:00:00',
|
||||
accountNumber: '0123456789',
|
||||
transferType: 'in',
|
||||
transferAmount: 100000,
|
||||
referenceCode: 'REF123',
|
||||
content: 'Order payment'
|
||||
};
|
||||
|
||||
const result = verifier.process(payload);
|
||||
runner.assert(result.success === true, 'Should verify successfully');
|
||||
runner.assert(result.transaction.id === 12345, 'Should parse transaction ID');
|
||||
runner.assert(result.isIncoming === true, 'Should detect incoming transfer');
|
||||
});
|
||||
|
||||
runner.test('should reject invalid SePay transfer type', () => {
|
||||
const verifier = new SePayWebhookVerifier('none');
|
||||
const payload = {
|
||||
id: 12345,
|
||||
gateway: 'Vietcombank',
|
||||
transactionDate: '2025-01-13 10:00:00',
|
||||
accountNumber: '0123456789',
|
||||
transferType: 'invalid',
|
||||
transferAmount: 100000,
|
||||
referenceCode: 'REF123'
|
||||
};
|
||||
|
||||
const result = verifier.process(payload);
|
||||
runner.assert(result.success === false, 'Should fail validation');
|
||||
runner.assert(result.error.includes('Invalid transferType'), 'Should report invalid transfer type');
|
||||
});
|
||||
|
||||
runner.test('should verify SePay webhook with API key', () => {
|
||||
const verifier = new SePayWebhookVerifier('api_key', 'test_key_123');
|
||||
const payload = {
|
||||
id: 12345,
|
||||
gateway: 'Vietcombank',
|
||||
transactionDate: '2025-01-13 10:00:00',
|
||||
accountNumber: '0123456789',
|
||||
transferType: 'in',
|
||||
transferAmount: 100000,
|
||||
referenceCode: 'REF123'
|
||||
};
|
||||
|
||||
const headers = { Authorization: 'Apikey test_key_123' };
|
||||
const result = verifier.process(payload, headers);
|
||||
runner.assert(result.success === true, 'Should verify with valid API key');
|
||||
});
|
||||
|
||||
runner.test('should reject SePay webhook with invalid API key', () => {
|
||||
const verifier = new SePayWebhookVerifier('api_key', 'test_key_123');
|
||||
const payload = {
|
||||
id: 12345,
|
||||
gateway: 'Vietcombank',
|
||||
transactionDate: '2025-01-13 10:00:00',
|
||||
accountNumber: '0123456789',
|
||||
transferType: 'in',
|
||||
transferAmount: 100000,
|
||||
referenceCode: 'REF123'
|
||||
};
|
||||
|
||||
const headers = { Authorization: 'Apikey wrong_key' };
|
||||
const result = verifier.process(payload, headers);
|
||||
runner.assert(result.success === false, 'Should reject invalid API key');
|
||||
});
|
||||
|
||||
// Polar Webhook Verifier Tests
|
||||
console.log('\nPolar Webhook Verifier Tests:');
|
||||
|
||||
runner.test('should verify valid Polar webhook', () => {
|
||||
const crypto = require('crypto');
|
||||
const secret = Buffer.from('test_secret_key').toString('base64');
|
||||
const verifier = new PolarWebhookVerifier(secret);
|
||||
|
||||
const payload = JSON.stringify({
|
||||
type: 'order.paid',
|
||||
data: { id: 'order_123', amount: 2000 }
|
||||
});
|
||||
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const signedPayload = `${timestamp}.${payload}`;
|
||||
const signature = crypto
|
||||
.createHmac('sha256', Buffer.from(secret, 'base64'))
|
||||
.update(signedPayload)
|
||||
.digest('base64');
|
||||
|
||||
const headers = {
|
||||
'webhook-id': 'msg_123',
|
||||
'webhook-timestamp': timestamp.toString(),
|
||||
'webhook-signature': `v1=${signature}`
|
||||
};
|
||||
|
||||
const result = verifier.process(payload, headers);
|
||||
if (!result.success) {
|
||||
throw new Error(`Verification failed: ${result.error}`);
|
||||
}
|
||||
runner.assert(result.success === true, 'Should verify successfully');
|
||||
runner.assertEqual(result.event.type, 'order.paid', 'Should parse event type');
|
||||
});
|
||||
|
||||
runner.test('should reject Polar webhook with invalid signature', () => {
|
||||
const secret = Buffer.from('test_secret_key').toString('base64');
|
||||
const verifier = new PolarWebhookVerifier(secret);
|
||||
|
||||
const payload = JSON.stringify({
|
||||
type: 'order.paid',
|
||||
data: { id: 'order_123' }
|
||||
});
|
||||
|
||||
const headers = {
|
||||
'webhook-id': 'msg_123',
|
||||
'webhook-timestamp': Math.floor(Date.now() / 1000).toString(),
|
||||
'webhook-signature': 'v1=invalid_signature'
|
||||
};
|
||||
|
||||
const result = verifier.process(payload, headers);
|
||||
runner.assert(result.success === false, 'Should reject invalid signature');
|
||||
});
|
||||
|
||||
runner.test('should categorize Polar event types', () => {
|
||||
runner.assertEqual(PolarWebhookVerifier.getEventCategory('order.paid'), 'order');
|
||||
runner.assertEqual(PolarWebhookVerifier.getEventCategory('subscription.active'), 'subscription');
|
||||
runner.assertEqual(PolarWebhookVerifier.getEventCategory('customer.created'), 'customer');
|
||||
runner.assert(PolarWebhookVerifier.isPaymentEvent('order.paid') === true);
|
||||
runner.assert(PolarWebhookVerifier.isSubscriptionEvent('subscription.active') === true);
|
||||
});
|
||||
|
||||
// Checkout Helper Tests
|
||||
console.log('\nCheckout Helper Tests:');
|
||||
|
||||
runner.test('should generate SePay checkout fields', () => {
|
||||
const config = {
|
||||
merchantId: 'SP-TEST-123',
|
||||
secretKey: 'test_secret',
|
||||
orderInvoiceNumber: 'ORD001',
|
||||
orderAmount: 100000,
|
||||
successUrl: 'https://example.com/success',
|
||||
errorUrl: 'https://example.com/error',
|
||||
cancelUrl: 'https://example.com/cancel',
|
||||
env: 'sandbox'
|
||||
};
|
||||
|
||||
const result = CheckoutHelper.generateSePayCheckout(config);
|
||||
runner.assert(result.fields !== undefined, 'Should generate fields');
|
||||
runner.assert(result.fields.signature !== undefined, 'Should generate signature');
|
||||
runner.assertEqual(result.fields.merchant_id, 'SP-TEST-123', 'Should include merchant ID');
|
||||
runner.assert(result.formUrl.includes('sandbox'), 'Should use sandbox URL');
|
||||
});
|
||||
|
||||
runner.test('should generate Polar checkout config', () => {
|
||||
const config = {
|
||||
productPriceId: 'price_123',
|
||||
successUrl: 'https://example.com/success',
|
||||
externalCustomerId: 'user_123',
|
||||
accessToken: 'test_token',
|
||||
server: 'sandbox'
|
||||
};
|
||||
|
||||
const result = CheckoutHelper.generatePolarCheckout(config);
|
||||
runner.assert(result.config !== undefined, 'Should generate config');
|
||||
runner.assertEqual(result.config.product_price_id, 'price_123', 'Should include price ID');
|
||||
runner.assertEqual(result.config.external_customer_id, 'user_123', 'Should include customer ID');
|
||||
runner.assert(result.apiEndpoint.includes('sandbox'), 'Should use sandbox endpoint');
|
||||
});
|
||||
|
||||
runner.test('should reject Polar config with relative URL', () => {
|
||||
try {
|
||||
CheckoutHelper.generatePolarCheckout({
|
||||
productPriceId: 'price_123',
|
||||
successUrl: '/success' // Relative URL
|
||||
});
|
||||
runner.assert(false, 'Should throw error for relative URL');
|
||||
} catch (error) {
|
||||
runner.assert(error.message.includes('absolute URL'), 'Should require absolute URL');
|
||||
}
|
||||
});
|
||||
|
||||
// Run summary
|
||||
const success = runner.summary();
|
||||
process.exit(success ? 0 : 1);
|
||||
Reference in New Issue
Block a user