Files
iyzico/tests/integration/subscription.test.ts
Duhan BALCI c65195d26d init
2026-01-01 18:30:21 +03:00

557 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Subscription integration tests
*
* NOTE: These tests require the subscription feature to be enabled in the iyzico merchant panel.
* If the feature is not enabled, tests will be skipped automatically.
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createTestClient, assertSuccessResponse } from '../setup';
import {
testProductCreateRequest,
testPricingPlanCreateRequest,
testSubscriptionCustomer,
testSubscriptionPaymentCard,
generateTestProductName,
generateTestPlanName,
generateTestEmail,
} from '../fixtures/subscription';
import { IyzicoResponseError } from '../../src/errors';
describe('Subscription Integration Tests', () => {
const client = createTestClient();
// Flag to track if subscription feature is available
let subscriptionFeatureAvailable = true;
// Shared references for chained tests
let createdProductReferenceCode: string | undefined;
let createdPricingPlanReferenceCode: string | undefined;
let createdSubscriptionReferenceCode: string | undefined;
let createdCustomerReferenceCode: string | undefined;
// Helper to skip test if subscription feature is not available
const skipIfUnavailable = () => {
if (!subscriptionFeatureAvailable) {
console.log('Skipping: Subscription feature not enabled in merchant panel');
return true;
}
return false;
};
// ============================================================================
// Product CRUD Tests
// ============================================================================
describe('Product Operations', () => {
it('should create a subscription product', async () => {
const request = {
...testProductCreateRequest,
// conversationId: `test-product-create-${Date.now()}`,
// name: generateTestProductName(),
};
try {
const response = await client.subscription.createProduct(request);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBeTruthy();
expect(response.data?.name).toBe(request.name);
expect(response.data?.status).toBe('ACTIVE');
// Save for later tests
createdProductReferenceCode = response.data?.referenceCode;
} catch (error) {
// Check if subscription feature is not enabled (error code 100001 = system error)
if (error instanceof IyzicoResponseError && error.errorResponse?.errorCode === '100001') {
subscriptionFeatureAvailable = false;
console.log('Subscription feature not enabled in merchant panel. Skipping subscription tests.');
return; // Skip this test gracefully
}
throw error;
}
});
it('should get a subscription product', async () => {
if (skipIfUnavailable() || !createdProductReferenceCode) {
return; // Skip if feature unavailable or no product created
}
const response = await client.subscription.getProduct(createdProductReferenceCode);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBe(createdProductReferenceCode);
});
it('should update a subscription product', async () => {
if (skipIfUnavailable() || !createdProductReferenceCode) {
return; // Skip if feature unavailable or no product created
}
const updatedName = `Updated Product ${Date.now()}`;
const response = await client.subscription.updateProduct(createdProductReferenceCode, {
name: updatedName,
description: 'Updated description',
conversationId: `test-product-update-${Date.now()}`,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.name).toBe(updatedName);
});
it('should list subscription products', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
const response = await client.subscription.listProducts({
page: 1,
count: 10,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.items).toBeInstanceOf(Array);
expect(response.data?.totalCount).toBeGreaterThanOrEqual(0);
});
});
// ============================================================================
// Pricing Plan CRUD Tests
// ============================================================================
describe('Pricing Plan Operations', () => {
it('should create a pricing plan', async () => {
if (skipIfUnavailable() || !createdProductReferenceCode) {
return; // Skip if feature unavailable or no product created
}
const request = {
...testPricingPlanCreateRequest,
conversationId: `test-plan-create-${Date.now()}`,
name: generateTestPlanName(),
};
const response = await client.subscription.createPricingPlan(createdProductReferenceCode, request);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBeTruthy();
expect(response.data?.name).toBe(request.name);
expect(response.data?.price).toBe(request.price);
expect(response.data?.paymentInterval).toBe(request.paymentInterval);
// Save for later tests
createdPricingPlanReferenceCode = response.data?.referenceCode;
});
it('should get a pricing plan', async () => {
if (skipIfUnavailable() || !createdPricingPlanReferenceCode) {
return; // Skip if feature unavailable or no plan created
}
const response = await client.subscription.getPricingPlan(createdPricingPlanReferenceCode);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBe(createdPricingPlanReferenceCode);
});
it('should update a pricing plan', async () => {
if (skipIfUnavailable() || !createdPricingPlanReferenceCode) {
return; // Skip if feature unavailable or no plan created
}
const updatedName = `Updated Plan ${Date.now()}`;
const response = await client.subscription.updatePricingPlan(createdPricingPlanReferenceCode, {
name: updatedName,
trialPeriodDays: 14,
conversationId: `test-plan-update-${Date.now()}`,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.name).toBe(updatedName);
});
it('should list pricing plans for a product', async () => {
if (skipIfUnavailable() || !createdProductReferenceCode) {
return; // Skip if feature unavailable or no product created
}
const response = await client.subscription.listPricingPlans(createdProductReferenceCode, {
page: 1,
count: 10,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.items).toBeInstanceOf(Array);
});
});
// ============================================================================
// Subscription Lifecycle Tests
// ============================================================================
describe('Subscription Operations', () => {
it('should initialize a subscription', async () => {
if (skipIfUnavailable() || !createdPricingPlanReferenceCode) {
return; // Skip if feature unavailable or no plan created
}
const uniqueEmail = generateTestEmail();
const response = await client.subscription.initialize({
locale: 'tr',
conversationId: `test-sub-init-${Date.now()}`,
pricingPlanReferenceCode: createdPricingPlanReferenceCode,
subscriptionInitialStatus: 'ACTIVE',
customer: {
...testSubscriptionCustomer,
email: uniqueEmail,
},
paymentCard: testSubscriptionPaymentCard,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBeTruthy();
expect(response.data?.customerReferenceCode).toBeTruthy();
// Save for later tests
createdSubscriptionReferenceCode = response.data?.referenceCode;
createdCustomerReferenceCode = response.data?.customerReferenceCode;
});
it('should get a subscription', async () => {
if (skipIfUnavailable() || !createdSubscriptionReferenceCode) {
return; // Skip if feature unavailable or no subscription created
}
const response = await client.subscription.get(createdSubscriptionReferenceCode);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
});
it('should list subscriptions', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
const response = await client.subscription.list({
page: 1,
count: 10,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.items).toBeInstanceOf(Array);
});
it('should list subscriptions with filters', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
const response = await client.subscription.list({
subscriptionStatus: 'ACTIVE',
page: 1,
count: 10,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
});
it('should initialize subscription with checkout form', async () => {
if (skipIfUnavailable() || !createdPricingPlanReferenceCode) {
return; // Skip if feature unavailable or no plan created
}
const uniqueEmail = generateTestEmail();
const response = await client.subscription.initializeCheckoutForm({
locale: 'tr',
conversationId: `test-sub-checkout-${Date.now()}`,
callbackUrl: 'https://www.merchant.com/callback',
pricingPlanReferenceCode: createdPricingPlanReferenceCode,
subscriptionInitialStatus: 'ACTIVE',
customer: {
...testSubscriptionCustomer,
email: uniqueEmail,
},
});
assertSuccessResponse(response);
expect(response.token).toBeTruthy();
expect(response.checkoutFormContent).toBeTruthy();
expect(response.tokenExpireTime).toBeGreaterThan(0);
});
it('should cancel a subscription', async () => {
if (skipIfUnavailable() || !createdSubscriptionReferenceCode) {
return; // Skip if feature unavailable or no subscription created
}
const response = await client.subscription.cancel(createdSubscriptionReferenceCode);
assertSuccessResponse(response);
});
});
// ============================================================================
// Customer Operations Tests
// ============================================================================
describe('Customer Operations', () => {
it('should list customers', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
const response = await client.subscription.listCustomers({
page: 1,
count: 10,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.items).toBeInstanceOf(Array);
});
it('should get a customer', async () => {
if (skipIfUnavailable() || !createdCustomerReferenceCode) {
return; // Skip if feature unavailable or no customer created
}
const response = await client.subscription.getCustomer(createdCustomerReferenceCode);
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.referenceCode).toBe(createdCustomerReferenceCode);
});
it('should update a customer', async () => {
if (skipIfUnavailable() || !createdCustomerReferenceCode) {
return; // Skip if feature unavailable or no customer created
}
const response = await client.subscription.updateCustomer(createdCustomerReferenceCode, {
name: 'Updated John',
surname: 'Updated Doe',
conversationId: `test-customer-update-${Date.now()}`,
});
assertSuccessResponse(response);
expect(response.data).toBeDefined();
expect(response.data?.name).toBe('Updated John');
});
});
// ============================================================================
// Card Update Tests
// ============================================================================
describe('Card Update Operations', () => {
it('should initialize card update checkout form', async () => {
if (skipIfUnavailable() || !createdCustomerReferenceCode) {
return; // Skip if feature unavailable or no customer created
}
try {
const response = await client.subscription.initializeCardUpdate({
locale: 'tr',
conversationId: `test-card-update-${Date.now()}`,
callbackUrl: 'https://www.merchant.com/callback',
customerReferenceCode: createdCustomerReferenceCode,
});
assertSuccessResponse(response);
expect(response.token).toBeTruthy();
expect(response.checkoutFormContent).toBeTruthy();
} catch (error) {
// Card update requires an active subscription - may fail if subscription was cancelled
if (error instanceof IyzicoResponseError) {
const errorMessage = error.errorResponse?.errorMessage || '';
if (errorMessage.includes('aktif abonelik bulunamadı')) {
console.log('Skipping: No active subscription found for card update test');
return;
}
}
throw error;
}
});
});
// ============================================================================
// Cleanup - Delete created resources
// ============================================================================
describe('Cleanup', () => {
it('should delete pricing plan', async () => {
if (skipIfUnavailable() || !createdPricingPlanReferenceCode) {
console.log('No pricing plan to delete');
return;
}
try {
const response = await client.subscription.deletePricingPlan(createdPricingPlanReferenceCode);
assertSuccessResponse(response);
} catch (error) {
// Deletion may fail if there are active subscriptions - this is expected
if (error instanceof IyzicoResponseError) {
console.log(`Could not delete pricing plan: ${error.errorResponse?.errorMessage || error.message}`);
return; // Don't fail the test for cleanup issues
}
throw error;
}
});
it('should delete product', async () => {
if (skipIfUnavailable() || !createdProductReferenceCode) {
console.log('No product to delete');
return;
}
try {
const response = await client.subscription.deleteProduct(createdProductReferenceCode);
assertSuccessResponse(response);
} catch (error) {
// Deletion may fail if there are active pricing plans/subscriptions - this is expected
if (error instanceof IyzicoResponseError) {
console.log(`Could not delete product: ${error.errorResponse?.errorMessage || error.message}`);
return; // Don't fail the test for cleanup issues
}
throw error;
}
});
});
// ============================================================================
// Edge Cases and Error Handling
// ============================================================================
describe('Edge Cases - Invalid Inputs', () => {
it('should handle non-existent product reference code', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.getProduct('invalid-product-ref-12345')
).rejects.toThrow(IyzicoResponseError);
});
it('should handle non-existent pricing plan reference code', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.getPricingPlan('invalid-plan-ref-12345')
).rejects.toThrow(IyzicoResponseError);
});
it('should handle non-existent subscription reference code', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.get('invalid-sub-ref-12345')
).rejects.toThrow(IyzicoResponseError);
});
it('should handle non-existent customer reference code', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.getCustomer('invalid-customer-ref-12345')
).rejects.toThrow(IyzicoResponseError);
});
it('should handle missing required fields in product creation', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.createProduct({
name: '',
conversationId: `test-empty-name-${Date.now()}`,
})
).rejects.toThrow(IyzicoResponseError);
});
it('should handle invalid pricing plan creation without product', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.createPricingPlan('invalid-product-ref', {
...testPricingPlanCreateRequest,
conversationId: `test-invalid-product-${Date.now()}`,
})
).rejects.toThrow(IyzicoResponseError);
});
it('should handle subscription initialization with invalid plan', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
const uniqueEmail = generateTestEmail();
await expect(
client.subscription.initialize({
locale: 'tr',
conversationId: `test-invalid-plan-${Date.now()}`,
pricingPlanReferenceCode: 'invalid-plan-ref',
subscriptionInitialStatus: 'ACTIVE',
customer: {
...testSubscriptionCustomer,
email: uniqueEmail,
},
paymentCard: testSubscriptionPaymentCard,
})
).rejects.toThrow(IyzicoResponseError);
});
it('should handle retry payment with invalid reference', async () => {
if (skipIfUnavailable()) {
return; // Skip if feature unavailable
}
await expect(
client.subscription.retryPayment({
referenceCode: 'invalid-order-ref-12345',
conversationId: `test-invalid-retry-${Date.now()}`,
})
).rejects.toThrow(IyzicoResponseError);
});
});
});