const soap = require('strong-soap').soap; const axios = require('axios'); const crypto = require('crypto'); const fs = require('fs'); class DomainNameAPI { static VERSION = '2.0.13'; constructor( userName = 'ownername', password = 'ownerpass', testMode = false) { this.serviceUsername = userName; this.servicePassword = password; this.serviceUrl = 'https://whmcs.domainnameapi.com/DomainApi.svc?singlewsdl'; this.soapClientPromise = this.createSoapClient(); } createSoapClient() { return new Promise((resolve, reject) => { const options = { strictSSL : false, wsdl_options: { timeout: 20000, }, }; soap.createClient(this.serviceUrl, options, (err, client) => { if (err) { console.error('SOAP Client Creation Error:', err); reject(new Error(`SOAP Connection Error: ${err.message}`)); } else { resolve(client); } }); }); } async AddChildNameServer(domainName, nameServer, ipAddress) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, ChildNameServer: nameServer, IpAddressList: [ipAddress] } }; // callApiFunction ile API çağrısı yapıyoruz return this.callApiFunction('AddChildNameServer', parameters).then((response) => { return { data: { NameServer: parameters.request.ChildNameServer, IPAdresses: parameters.request.IpAddressList }, result: true }; }); } /** * Delete Child Name Server for domain * @param {string} domainName * @param {string} nameServer * @return {Promise} */ async DeleteChildNameServer(domainName, nameServer) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, ChildNameServer: nameServer } }; // callApiFunction ile API çağrısı yapıyoruz return this.callApiFunction('DeleteChildNameServer', parameters).then((response) => { return { data: { NameServer: parameters.request.ChildNameServer }, result: true }; }); } /** * Modify Child Name Server for domain * @param {string} domainName * @param {string} nameServer * @param {string} ipAddress * @return {Promise} */ async ModifyChildNameServer(domainName, nameServer, ipAddress) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, ChildNameServer: nameServer, IpAddressList: [ipAddress] } }; // callApiFunction ile API çağrısı yapıyoruz return this.callApiFunction('ModifyChildNameServer', parameters).then((response) => { return { data: { NameServer: parameters.request.ChildNameServer, IPAdresses: parameters.request.IpAddressList }, result: true }; }); } /** * Get Contacts for domain, Administrative, Billing, Technical, Registrant * segments will be returned * @param {string} domainName * @return {Promise} */ async GetContacts(domainName) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName } }; return this.callApiFunction('GetContacts', parameters).then((response) => { const data = response[Object.keys(response)[0]]; let result = {}; // Eğer ContactInfo geçerli bir nesne ise if (data.AdministrativeContact && typeof data.AdministrativeContact === 'object' && data.TechnicalContact && typeof data.TechnicalContact === 'object' && data.RegistrantContact && typeof data.RegistrantContact === 'object' && data.BillingContact && typeof data.BillingContact === 'object') { result = { data: { contacts: { Administrative: this.parseContactInfo(data.AdministrativeContact), Billing: this.parseContactInfo(data.BillingContact), Registrant: this.parseContactInfo(data.RegistrantContact), Technical: this.parseContactInfo(data.TechnicalContact), } }, result: true }; } else { result = data; } return result; }); } /** * Save Contacts for domain, Contacts segments will be saved as Administrative, * Billing, Technical, Registrant. * @param {string} domainName * @param {Object} contacts * @return {Promise} */ async SaveContacts(domainName, contacts) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, AdministrativeContact: contacts.Administrative, BillingContact: contacts.Billing, TechnicalContact: contacts.Technical, RegistrantContact: contacts.Registrant } }; return this.callApiFunction('SaveContacts', parameters).then((response) => { const data = response[Object.keys(response)[0]]; let result = {}; if (data.OperationResult === 'SUCCESS') { result = { result: true }; } else { // Hata durumunu ayarla result = data; } return result; }); } /** * Transfer domain with EPP code and period * @param {string} domainName * @param {string} eppCode * @param {string} period * @return {Promise} */ async Transfer(domainName, eppCode, period) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, AuthCode: eppCode, AdditionalAttributes: { KeyValueOfstringstring: [ { Key: 'TRANSFERPERIOD', Value: period } ] } } }; return this.callApiFunction('Transfer', parameters).then((response) => { const data = response[Object.keys(response)[0]]; let result = {}; // Eğer DomainInfo geçerli bir nesne ise if (data.DomainInfo && typeof data.DomainInfo === 'object') { // Domain bilgisini ayrıştır ve döndür result = { result: true, data: this.parseDomainInfo(data.DomainInfo) }; } else { // Hata durumunu ayarla result = data; } return result; }); } /** * Stops Incoming Transfer * @param {string} domainName * @return {Promise} */ async CancelTransfer(domainName) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName } }; return this.callApiFunction('CancelTransfer', parameters).then((response) => { const data = response[Object.keys(response)[0]]; return { result: data.result , data: { DomainName: parameters.request.DomainName } }; }); } /** * Approve Outgoing transfer * @param {string} domainName * @return {Promise} */ async ApproveTransfer(domainName) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName } }; return this.callApiFunction('ApproveTransfer', parameters).then((response) => { const data = response[Object.keys(response)[0]]; return { result: data.result, data: { DomainName: parameters.request.DomainName } }; }); } /** * Reject Outgoing transfer * @param {string} domainName * @return {Promise} */ async RejectTransfer(domainName) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName } }; return this.callApiFunction('RejectTransfer', parameters).then((response) => { const data = response[Object.keys(response)[0]]; return { result: data.result, data: { DomainName: parameters.request.DomainName } }; }); } /** * Renew domain * @param {string} domainName * @param {number} period * @return {Promise} */ async Renew(domainName, period) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, Period: period } }; return this.callApiFunction('Renew', parameters).then((response) => { const data = response[Object.keys(response)[0]]; if (data.ExpirationDate) { return { result: true, data: { ExpirationDate: data.ExpirationDate } }; } else { return data; } }); } /** * Register domain with contact information * @param {string} domainName * @param {number} period * @param {Object} contacts * @param {Array} nameServers * @param {boolean} eppLock * @param {boolean} privacyLock * @param {Object} additionalAttributes * @return {Promise} */ async RegisterWithContactInfo(domainName, period, contacts, nameServers = ["dns.domainnameapi.com", "web.domainnameapi.com"], eppLock = true, privacyLock = false, additionalAttributes = {}) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, Period: period, NameServerList: { string: nameServers }, LockStatus: eppLock, PrivacyProtectionStatus: privacyLock, AdministrativeContact: contacts.Administrative, BillingContact: contacts.Billing, TechnicalContact: contacts.Technical, RegistrantContact: contacts.Registrant } }; // Format AdditionalAttributes correctly if (Object.keys(additionalAttributes).length > 0) { parameters.request.AdditionalAttributes = { KeyValueOfstringstring: Object.entries(additionalAttributes).map(([key, value]) => ({ Key: key, Value: value })) }; } // API çağrısı yap ve yanıtı işle return this.callApiFunction('RegisterWithContactInfo', parameters).then((response) => { // Handle raw number response if (typeof response === 'number') { return { result: false, level: 'error', message: `Registration failed with code ${response}`, ErrorCode: response }; } const data = response; let result = {}; // Eğer DomainInfo geçerli bir nesne ise if (data.DomainInfo && typeof data.DomainInfo === 'object') { result = { result: true, data: this.parseDomainInfo(data.DomainInfo) }; } else { // Hata durumunu ayarla result = { result: false, level: 'error', message: data.OperationMessage || 'Registration failed', ErrorCode: data.ErrorCode || response }; } return result; }); } /** * Modify privacy protection status of domain * @param {string} domainName * @param {boolean} status * @param {string} [reason="Owner request"] * @return {Promise} */ async ModifyPrivacyProtectionStatus(domainName, status, reason = "Owner request") { // Eğer reason boşsa varsayılan değeri ayarla if (reason.trim() === "") { reason = "Owner request"; } const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName, ProtectPrivacy: status, Reason: reason } }; // API çağrısı ve yanıt işleme return this.callApiFunction('ModifyPrivacyProtectionStatus', parameters).then((response) => { return { data: { PrivacyProtectionStatus: parameters.request.ProtectPrivacy }, result: true }; }); } /** * Sync from registry, domain information will be updated from registry * @param {string} domainName * @return {Promise} */ async SyncFromRegistry(domainName) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, DomainName: domainName } }; // API çağrısı ve yanıt işleme return this.callApiFunction('SyncFromRegistry', parameters).then((response) => { const data = response; let result = {}; // Eğer DomainInfo geçerli bir nesne ise if (data.DomainInfo && typeof data.DomainInfo === 'object') { result = { data: this.parseDomainInfo(data.DomainInfo), result: true }; } else { // Hata durumu result = data; } return result; }); } /** * Get Current primary Balance for your account * @param {string|number} currencyId * @return {Promise} */ async GetCurrentBalance(currencyId = 2) { if (currencyId.toString().toUpperCase() === 'USD') { currencyId = 2; } else if (['TRY', 'TL', '1'].includes(currencyId.toString().toUpperCase())) { currencyId = 1; } else { currencyId = 2; } const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, CurrencyId: currencyId } }; return this.callApiFunction('GetCurrentBalance', parameters).then((response) => { return response; }); } /** * Check Availability, SLD and TLD must be in array * @param {Array} domains * @param {Array} extensions * @param {number} period * @param {string} command * @return {Promise>} */ async CheckAvailability(domains, extensions, period=1, command='create') { const parameters = { request: { Password : this.servicePassword, UserName : this.serviceUsername, DomainNameList: { string: domains, // Array of domains }, TldList : { string: extensions, // Array of extensions }, Period : period, Commad : command, }, }; return this.callApiFunction('CheckAvailability', parameters).then((response) => { const data = response; const available = []; if (data.DomainAvailabilityInfoList?.DomainAvailabilityInfo?.Tld) { const buffer = data.DomainAvailabilityInfoList.DomainAvailabilityInfo; data.DomainAvailabilityInfoList = { DomainAvailabilityInfo: [buffer] }; }else{ } data.DomainAvailabilityInfoList.DomainAvailabilityInfo.forEach(value => { available.push({ TLD: value.Tld, DomainName: value.DomainName, Status: value.Status, Command: value.Command, Period: value.Period, IsFee: value.IsFee, Price: value.Price, Currency: value.Currency, Reason: value.Reason }); }); return available; }); } /** * Get Domain List of your account * @param {Object} extraParameters * @return {Promise} */ async GetList(extraParameters = {}) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, ...extraParameters } }; return this.callApiFunction('GetList', parameters).then((response) => { const data = response; let result = {}; if (data.TotalCount && Number.isInteger(data.TotalCount)) { result.data = { domains: [] }; if (data.DomainInfoList?.DomainInfo?.Id) { result.data.domains.push(data.DomainInfoList.DomainInfo); } else { data.DomainInfoList.DomainInfo.forEach(domainInfo => { result.data.domains.push(this.parseDomainInfo(domainInfo)); }); } result.result = true; result.totalCount = data.TotalCount; } else { result= data; } return result; }); } /** * Return tld list and pricing matrix * @param {number} count * @return {Promise} */ async GetTldList(count = 20) { const parameters = { request: { Password: this.servicePassword, UserName: this.serviceUsername, IncludePriceDefinitions: 1, PageSize: count } }; return this.callApiFunction('GetTldList', parameters).then((response) => { const data = response; let result = {}; if (data.TldInfoList.TldInfo.length > 0) { const extensions = data.TldInfoList.TldInfo.map(v => { const pricing = {}; const currencies = {}; v.PriceInfoList.TldPriceInfo.forEach(vp => { pricing[vp.TradeType.toLowerCase()] = { [vp.Period]: vp.Price }; currencies[vp.TradeType.toLowerCase()] = vp.CurrencyName; }); return { id: v.Id, status: v.Status, maxchar: v.MaxCharacterCount, maxperiod: v.MaxRegistrationPeriod, minchar: v.MinCharacterCount, minperiod: v.MinRegistrationPeriod, tld: v.Name, pricing, currencies }; }); result = { data: extensions, result: true }; } else { result = data } return result; }); } /** * Get Domain details * @param {string} domainName * @return {Promise} */ async GetDetails(domainName) { const parameters = { request: { Password : this.servicePassword, UserName : this.serviceUsername, DomainName: domainName, }, }; return this.callApiFunction('GetDetails', parameters).then((response) => { const data = response; let result = {}; if (data.DomainInfo && typeof data.DomainInfo === 'object') { result.data = this.parseDomainInfo(data.DomainInfo); result.result = true; } else { result = data; } return result; }); } /** * Get Reseller Details * @returns {Promise<{result: boolean, name: (string|string|*), active: * boolean, id: (string|*)}>} * @constructor */ async GetResellerDetails() { const parameters = { request: { UserName : this.serviceUsername, Password : this.servicePassword, CurrencyId: 2, }, }; return this.callApiFunction('GetResellerDetails', parameters). then((data) => { if (data && data.ResellerInfo) { let resp = { result: true, id : data.ResellerInfo.Id, active: data.ResellerInfo.Status === 'Active', name : data.ResellerInfo.Name, }; let activeCurrency = data.ResellerInfo.BalanceInfoList.BalanceInfo[0]; let balances = []; data.ResellerInfo.BalanceInfoList.BalanceInfo.forEach((v) => { if (v.CurrencyName === data.ResellerInfo.CurrencyInfo.Code) { activeCurrency = v; } balances.push({ balance : v.Balance, currency: v.CurrencyName, symbol : v.CurrencySymbol, }); }); resp.balance = activeCurrency.Balance; resp.currency = activeCurrency.CurrencyName; resp.symbol = activeCurrency.CurrencySymbol; resp.balances = balances; // Şu noktada resp objesi işleme alınabilir return resp; } else { return {result: false, ...data}; } }); } /** * Parse domain information * @param {Object} data * @return {Object} */ parseDomainInfo(data) { const result = { ID: data.Id || "", Status: data.Status || "", DomainName: data.DomainName || "", AuthCode: data.Auth || "", LockStatus: typeof data.LockStatus === "boolean" ? data.LockStatus.toString() : "", PrivacyProtectionStatus: typeof data.PrivacyProtectionStatus === "boolean" ? data.PrivacyProtectionStatus.toString() : "", IsChildNameServer: typeof data.IsChildNameServer === "boolean" ? data.IsChildNameServer.toString() : "", Contacts: { Administrative: { ID: data.AdministrativeContactId || "" }, Billing: { ID: data.BillingContactId || "" }, Technical: { ID: data.TechnicalContactId || "" }, Registrant: { ID: data.RegistrantContactId || "" } }, Dates: { Start: data.StartDate || "", Expiration: data.ExpirationDate || "", RemainingDays: data.RemainingDay || "" }, NameServers: data.NameServerList ? (Array.isArray(data.NameServerList) ? data.NameServerList : [data.NameServerList]) : [], Additional: (data.AdditionalAttributes && data.AdditionalAttributes.KeyValueOfstringstring) ? (Array.isArray(data.AdditionalAttributes.KeyValueOfstringstring) ? data.AdditionalAttributes.KeyValueOfstringstring.reduce((acc, attr) => { if (attr.Key && attr.Value) acc[attr.Key] = attr.Value; return acc; }, {}) : { [data.AdditionalAttributes.KeyValueOfstringstring.Key]: data.AdditionalAttributes.KeyValueOfstringstring.Value }) : {}, ChildNameServers: data.ChildNameServerInfo ? (Array.isArray(data.ChildNameServerInfo) ? data.ChildNameServerInfo.map(server => ({ ns: server.ChildNameServer || "", ip: Array.isArray(server.IpAddress?.string) ? server.IpAddress.string : [server.IpAddress?.string || ""] })) : [{ ns: data.ChildNameServerInfo.ChildNameServer || "", ip: Array.isArray(data.ChildNameServerInfo.IpAddress?.string) ? data.ChildNameServerInfo.IpAddress.string : [data.ChildNameServerInfo.IpAddress?.string || ""] }]) : [] }; return result; } /** * Parse contact information * @param {Object} data * @return {Object} */ parseContactInfo(data) { return { ID : data.Id || '', Status : data.Status || '', Address : { Line1 : data.AddressLine1 || '', Line2 : data.AddressLine2 || '', Line3 : data.AddressLine3 || '', State : data.State || '', City : data.City || '', Country: data.Country || '', ZipCode: data.ZipCode || '', }, Phone : { Number : data.Phone || '', CountryCode: data.PhoneCountryCode || '', }, Fax : { Number : data.Fax || '', CountryCode: data.FaxCountryCode || '', }, AuthCode : data.Auth || '', FirstName: data.FirstName || '', LastName : data.LastName || '', Company : data.Company || '', EMail : data.EMail || '', Type : data.Type || '', }; } async callApiFunction(apiFunctionName, parameters) { try { const client = await this.soapClientPromise; // Dinamik olarak fonksiyon çağırma const result = await client[`${apiFunctionName}`](parameters); // Derinlik ne olursa olsun ...Result anahtarını bulalım const responseKey = `${apiFunctionName}Result`; let data = result[responseKey] || null; // İlk seviyede arar // Eğer ilk seviyede bulamazsak, ikinci seviyede ararız if (!data) { const firstKey = Object.keys(result)[0]; if (result[firstKey] && result[firstKey][responseKey]) { data = result[firstKey][responseKey]; } } if (!data || typeof data !== 'object') { return {result: false, level: 'fatal', message: 'No data returned', ErrorCode: 0}; } if (data.faultcode) { return {result: false, level: 'fault', message: data.faultstring, ErrorCode: data.faultcode}; } data.result = data.OperationResult === 'SUCCESS'; if (data.result === false) { data.message = data.OperationMessage || 'Operation failed'; data.level = 'error'; // Make sure error code is passed through if (typeof data === 'number') { data = { result: false, level: 'error', message: 'Operation failed', ErrorCode: data }; } } return data; } catch (error) { console.error('API Error:', error); return {result: false, level: 'exception', message: error.message, ErrorCode: error.code || 0}; } } } module.exports = DomainNameAPI;