nucleus-core-ts 0.9.701 → 0.9.702

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -86,7 +86,7 @@ and limitations under the License.
86
86
  `)){let trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))continue;let separatorIndex=trimmed.indexOf(":");if(separatorIndex===-1)continue;let key=trimmed.slice(0,separatorIndex),value=trimmed.slice(separatorIndex+1);fields[key]=value}return fields}toNumber(value){if(!value)return 0;let num=Number.parseFloat(value);return Number.isNaN(num)?0:num}}import*as fs3 from"fs";import*as os from"os";class SystemCollector{config;lastCpuInfo=null;constructor(config){this.config=config}async collect(){if(!this.config?.enabled)return null;let metrics={cpu:{usage:0,cores:0},memory:{total:0,used:0,free:0,usagePercent:0,heapUsed:0,heapTotal:0},disk:{total:0,used:0,free:0,usagePercent:0},network:{bytesIn:0,bytesOut:0},process:{uptime:0,pid:0,eventLoopLag:0}};if(this.config.metrics?.cpu!==!1)metrics.cpu=this.collectCpu();if(this.config.metrics?.memory!==!1)metrics.memory=this.collectMemory();if(this.config.metrics?.disk!==!1)metrics.disk=await this.collectDisk();if(this.config.metrics?.network)metrics.network=this.collectNetwork();if(this.config.metrics?.process!==!1)metrics.process=await this.collectProcess();return metrics}collectCpu(){let cpus2=os.cpus(),cores=cpus2.length,idle=0,total=0;for(let cpu of cpus2)idle+=cpu.times.idle,total+=cpu.times.user+cpu.times.nice+cpu.times.sys+cpu.times.idle+cpu.times.irq;let usage=0;if(this.lastCpuInfo){let idleDiff=idle-this.lastCpuInfo.idle,totalDiff=total-this.lastCpuInfo.total;usage=totalDiff>0?Math.round((1-idleDiff/totalDiff)*100*100)/100:0}return this.lastCpuInfo={idle,total},{usage,cores}}collectMemory(){let total=os.totalmem(),free=os.freemem(),used=total-free,usagePercent=Math.round(used/total*100*100)/100,memUsage=process.memoryUsage();return{total,used,free,usagePercent,heapUsed:memUsage.heapUsed,heapTotal:memUsage.heapTotal}}async collectDisk(){try{let stats=fs3.statfsSync("/"),total=stats.blocks*stats.bsize,free=stats.bfree*stats.bsize,used=total-free,usagePercent=Math.round(used/total*100*100)/100;return{total,used,free,usagePercent}}catch{return{total:0,used:0,free:0,usagePercent:0}}}collectNetwork(){try{let content=fs3.readFileSync("/proc/net/dev","utf-8"),bytesIn=0,bytesOut=0;for(let line of content.split(`
87
87
  `).slice(2)){let[namePart,dataPart]=line.split(":");if(!namePart||!dataPart)continue;if(namePart.trim()==="lo")continue;let fields=dataPart.trim().split(/\s+/);bytesIn+=Number.parseInt(fields[0]??"0",10)||0,bytesOut+=Number.parseInt(fields[8]??"0",10)||0}return{bytesIn,bytesOut}}catch{return{bytesIn:0,bytesOut:0}}}async collectProcess(){let uptime=process.uptime(),pid=process.pid,lagStart=Date.now(),eventLoopLag=await new Promise((resolve)=>{setImmediate(()=>{resolve(Date.now()-lagStart)})});return{uptime,pid,eventLoopLag}}}var init_SystemCollector=()=>{};import{randomUUID as randomUUID3}from"crypto";import*as os2 from"os";class LiveMonitoringService{store;memoryInterval=null;cpuInterval=null;lastCpuInfo=null;isRunning=!1;constructor(config){let merged={...DEFAULT_LIVE_CONFIG,...config};this.store={requests:[],configs:{logMemory:merged.logMemory,logCpu:merged.logCpu,logDapr:merged.logDapr,logWebSocket:merged.logWebSocket,cpuLogInterval:merged.cpuLogInterval,memoryLogInterval:merged.memoryLogInterval},logs:{memory:[],cpu:[],dapr:[],ws:[]},logLimits:{memory:merged.memoryLogLimit,cpu:merged.cpuLogLimit,dapr:merged.daprLogLimit,ws:merged.wsLogLimit,request:merged.requestLogLimit},worker:{pid:process.pid,workerId:null,memory:null,cpu:null,updatedAt:Date.now()},allWorkers:[],daprEvents:[],wsEvents:[]}}start(){if(this.isRunning)return;if(this.isRunning=!0,this.store.configs.logMemory)this.startMemoryCollector();if(this.store.configs.logCpu)this.startCpuCollector()}stop(){if(!this.isRunning)return;if(this.isRunning=!1,this.memoryInterval)clearInterval(this.memoryInterval),this.memoryInterval=null;if(this.cpuInterval)clearInterval(this.cpuInterval),this.cpuInterval=null}startMemoryCollector(){if(this.memoryInterval)clearInterval(this.memoryInterval);let collect=()=>{if(!this.store.configs.logMemory)return;let mem=process.memoryUsage(),entry={timestamp:Date.now(),rss:mem.rss,heapUsed:mem.heapUsed,heapTotal:mem.heapTotal};if(this.store.logs.memory.push(entry),this.store.logs.memory.length>this.store.logLimits.memory*2)this.store.logs.memory=this.store.logs.memory.slice(-this.store.logLimits.memory);this.store.worker.memory=entry,this.store.worker.updatedAt=Date.now()};collect(),this.memoryInterval=setInterval(collect,this.store.configs.memoryLogInterval)}startCpuCollector(){if(this.cpuInterval)clearInterval(this.cpuInterval);let collect=()=>{if(!this.store.configs.logCpu)return;let cpus3=os2.cpus(),userTime=0,sysTime=0,idle=0,total=0;for(let cpu of cpus3)userTime+=cpu.times.user,sysTime+=cpu.times.sys,idle+=cpu.times.idle,total+=cpu.times.user+cpu.times.nice+cpu.times.sys+cpu.times.idle+cpu.times.irq;let userPercent=0,sysPercent=0;if(this.lastCpuInfo){let totalDiff=total-this.lastCpuInfo.total,idleDiff=idle-this.lastCpuInfo.idle;if(totalDiff>0){let activeDiff=totalDiff-idleDiff;userPercent=Math.round((userTime-0)/(activeDiff||1)*100*100)/100,sysPercent=Math.round((sysTime-0)/(activeDiff||1)*100*100)/100;let totalActive=Math.round((1-idleDiff/totalDiff)*100*100)/100;userPercent=Math.round(totalActive*0.7*100)/100,sysPercent=Math.round(totalActive*0.3*100)/100}}this.lastCpuInfo={idle,total};let entry={timestamp:Date.now(),user:userPercent,system:sysPercent};if(this.store.logs.cpu.push(entry),this.store.logs.cpu.length>this.store.logLimits.cpu*2)this.store.logs.cpu=this.store.logs.cpu.slice(-this.store.logLimits.cpu);this.store.worker.cpu=entry,this.store.worker.updatedAt=Date.now()};collect(),this.cpuInterval=setInterval(collect,this.store.configs.cpuLogInterval)}recordRequest(request){if(this.store.requests.push(request),this.store.requests.length>this.store.logLimits.request*2)this.store.requests=this.store.requests.slice(-this.store.logLimits.request)}recordDaprEvent(type,details){if(!this.store.configs.logDapr)return;let event={id:randomUUID3(),type,timestamp:Date.now(),...details};if(this.store.logs.dapr.push(event),this.store.daprEvents.push(event),this.store.logs.dapr.length>this.store.logLimits.dapr*2)this.store.logs.dapr=this.store.logs.dapr.slice(-this.store.logLimits.dapr);if(this.store.daprEvents.length>this.store.logLimits.dapr*2)this.store.daprEvents=this.store.daprEvents.slice(-this.store.logLimits.dapr)}recordWsEvent(type,details){if(!this.store.configs.logWebSocket)return;let event={id:randomUUID3(),type,timestamp:Date.now(),...details};if(this.store.logs.ws.push(event),this.store.wsEvents.push(event),this.store.logs.ws.length>this.store.logLimits.ws*2)this.store.logs.ws=this.store.logs.ws.slice(-this.store.logLimits.ws);if(this.store.wsEvents.length>this.store.logLimits.ws*2)this.store.wsEvents=this.store.wsEvents.slice(-this.store.logLimits.ws)}getSnapshot(){return{memory:this.store.logs.memory.slice(-this.store.logLimits.memory),cpu:this.store.logs.cpu.slice(-this.store.logLimits.cpu),requests:this.store.requests.slice(-this.store.logLimits.request),dapr:this.store.logs.dapr.slice(-this.store.logLimits.dapr),ws:this.store.logs.ws.slice(-this.store.logLimits.ws),workers:this.store.allWorkers.length?this.store.allWorkers:[this.store.worker],logLimits:{...this.store.logLimits},configs:{...this.store.configs}}}getUpdatesSince(timestamps){let memoryUpdates=this.store.logs.memory.filter((m)=>m.timestamp>timestamps.memory),cpuUpdates=this.store.logs.cpu.filter((c)=>c.timestamp>timestamps.cpu),requestUpdates=this.store.requests.filter((r)=>r.timestamp>timestamps.request),daprUpdates=this.store.logs.dapr.filter((d)=>d.timestamp>timestamps.dapr),wsUpdates=this.store.logs.ws.filter((w)=>w.timestamp>timestamps.ws);if(!(memoryUpdates.length>0||cpuUpdates.length>0||requestUpdates.length>0||daprUpdates.length>0||wsUpdates.length>0))return null;return{memory:memoryUpdates,cpu:cpuUpdates,requests:requestUpdates,dapr:daprUpdates,ws:wsUpdates,timestamp:Date.now()}}getLogs(){return{memory:this.store.logs.memory,cpu:this.store.logs.cpu,requests:this.store.requests,dapr:this.store.logs.dapr,ws:this.store.logs.ws,daprEvents:this.store.daprEvents,wsEvents:this.store.wsEvents,configs:{logMemory:this.store.configs.logMemory,logCpu:this.store.configs.logCpu,logDapr:this.store.configs.logDapr,logWebSocket:this.store.configs.logWebSocket},limits:{...this.store.logLimits}}}getSettings(){return{configs:{...this.store.configs},logLimits:{...this.store.logLimits}}}changeSettings(payload){if(payload.logMemory!==void 0)this.store.configs.logMemory=payload.logMemory;if(payload.logCpu!==void 0)this.store.configs.logCpu=payload.logCpu;if(payload.logDapr!==void 0)this.store.configs.logDapr=payload.logDapr;if(payload.logWebSocket!==void 0)this.store.configs.logWebSocket=payload.logWebSocket;if(payload.cpuLogInterval!==void 0){if(this.store.configs.cpuLogInterval=payload.cpuLogInterval,this.isRunning&&this.store.configs.logCpu)this.startCpuCollector()}if(payload.memoryLogInterval!==void 0){if(this.store.configs.memoryLogInterval=payload.memoryLogInterval,this.isRunning&&this.store.configs.logMemory)this.startMemoryCollector()}if(payload.memoryLogLimit!==void 0)this.store.logLimits.memory=payload.memoryLogLimit;if(payload.cpuLogLimit!==void 0)this.store.logLimits.cpu=payload.cpuLogLimit;if(payload.daprLogLimit!==void 0)this.store.logLimits.dapr=payload.daprLogLimit;if(payload.wsLogLimit!==void 0)this.store.logLimits.ws=payload.wsLogLimit;if(payload.requestLogLimit!==void 0)this.store.logLimits.request=payload.requestLogLimit;return{message:"Settings updated successfully",configs:{...this.store.configs},logLimits:{...this.store.logLimits}}}getStore(){return this.store}isEnabled(){return this.isRunning}}var DEFAULT_LIVE_CONFIG;var init_LiveMonitoringService=__esm(()=>{DEFAULT_LIVE_CONFIG={enabled:!0,logMemory:!0,logCpu:!0,logDapr:!0,logWebSocket:!0,memoryLogInterval:1000,cpuLogInterval:1000,memoryLogLimit:100,cpuLogLimit:100,daprLogLimit:100,wsLogLimit:100,requestLogLimit:100,streamInterval:150}});class MonitoringService{redis;logger;config;appId;flushToDb;systemCollector;applicationCollector;databaseCollector=null;redisCollector=null;alertService;collectInterval=null;flushInterval=null;pendingMetrics=[];isRunning=!1;constructor(deps){if(this.redis=deps.redis,this.logger=deps.logger,this.config=this.mergeConfig(deps.config),this.appId=deps.appId,this.flushToDb=deps.flushToDb,this.config.database.enabled)if(deps.dbQuery)this.databaseCollector=new DatabaseCollector(this.config.database,deps.dbQuery);else this.logger.warn("[Monitoring] monitoring.database is enabled but no database query executor is wired \u2014 database metrics are skipped");if(this.config.redis.enabled)if(deps.redis.getDirectClient())this.redisCollector=new RedisCollector(()=>deps.redis.getDirectClient());else this.logger.warn("[Monitoring] monitoring.redis is enabled but Redis runs in Dapr mode \u2014 INFO is unavailable, Redis metrics are skipped");if(this.config.persistence.enabled&&!deps.flushToDb)this.logger.warn("[Monitoring] monitoring.persistence is enabled but no flushToDb handler is wired \u2014 metrics are kept in Redis only");this.systemCollector=new SystemCollector(this.config.system),this.applicationCollector=new ApplicationCollector(this.config.application),this.alertService=new AlertService({logger:deps.logger,emailService:deps.emailService,config:this.config,appId:deps.appId})}mergeConfig(config){return{enabled:config.enabled??DEFAULT_CONFIG3.enabled,system:{enabled:config.system?.enabled??DEFAULT_CONFIG3.system.enabled,collectInterval:config.system?.collectInterval??DEFAULT_CONFIG3.system.collectInterval,metrics:{cpu:config.system?.metrics?.cpu??DEFAULT_CONFIG3.system.metrics.cpu,memory:config.system?.metrics?.memory??DEFAULT_CONFIG3.system.metrics.memory,disk:config.system?.metrics?.disk??DEFAULT_CONFIG3.system.metrics.disk,network:config.system?.metrics?.network??DEFAULT_CONFIG3.system.metrics.network,process:config.system?.metrics?.process??DEFAULT_CONFIG3.system.metrics.process}},application:{enabled:config.application?.enabled??DEFAULT_CONFIG3.application.enabled,metrics:{requests:config.application?.metrics?.requests??DEFAULT_CONFIG3.application.metrics.requests,responseTime:config.application?.metrics?.responseTime??DEFAULT_CONFIG3.application.metrics.responseTime,errors:config.application?.metrics?.errors??DEFAULT_CONFIG3.application.metrics.errors,rateLimits:config.application?.metrics?.rateLimits??DEFAULT_CONFIG3.application.metrics.rateLimits}},database:{enabled:config.database?.enabled??DEFAULT_CONFIG3.database.enabled,metrics:{connections:config.database?.metrics?.connections??DEFAULT_CONFIG3.database.metrics.connections,queryTime:config.database?.metrics?.queryTime??DEFAULT_CONFIG3.database.metrics.queryTime,slowQueryThreshold:config.database?.metrics?.slowQueryThreshold??DEFAULT_CONFIG3.database.metrics.slowQueryThreshold}},redis:{enabled:config.redis?.enabled??DEFAULT_CONFIG3.redis.enabled},persistence:{enabled:config.persistence?.enabled??DEFAULT_CONFIG3.persistence.enabled,flushInterval:config.persistence?.flushInterval??DEFAULT_CONFIG3.persistence.flushInterval,retentionDays:config.persistence?.retentionDays??DEFAULT_CONFIG3.persistence.retentionDays},alerts:{enabled:config.alerts?.enabled??DEFAULT_CONFIG3.alerts.enabled,email:{enabled:config.alerts?.email?.enabled??DEFAULT_CONFIG3.alerts.email.enabled,recipients:config.alerts?.email?.recipients??DEFAULT_CONFIG3.alerts.email.recipients},thresholds:{cpuPercent:config.alerts?.thresholds?.cpuPercent??DEFAULT_CONFIG3.alerts.thresholds.cpuPercent,memoryPercent:config.alerts?.thresholds?.memoryPercent??DEFAULT_CONFIG3.alerts.thresholds.memoryPercent,diskPercent:config.alerts?.thresholds?.diskPercent??DEFAULT_CONFIG3.alerts.thresholds.diskPercent,errorRatePercent:config.alerts?.thresholds?.errorRatePercent??DEFAULT_CONFIG3.alerts.thresholds.errorRatePercent,responseTimeMs:config.alerts?.thresholds?.responseTimeMs??DEFAULT_CONFIG3.alerts.thresholds.responseTimeMs,rateLimitBlocksPerMinute:config.alerts?.thresholds?.rateLimitBlocksPerMinute??DEFAULT_CONFIG3.alerts.thresholds.rateLimitBlocksPerMinute},cooldown:config.alerts?.cooldown??DEFAULT_CONFIG3.alerts.cooldown}}}parseTimeToMs(time){let match=time.match(/^(\d+)(ms|s|m|h|d)$/);if(!match||!match[1]||!match[2])return 1e4;let value=parseInt(match[1],10);switch(match[2]){case"ms":return value;case"s":return value*1000;case"m":return value*60*1000;case"h":return value*60*60*1000;case"d":return value*24*60*60*1000;default:return 1e4}}start(){if(!this.config.enabled||this.isRunning)return;this.isRunning=!0,this.logger.info("[Monitoring] Starting monitoring service");let collectIntervalMs=this.parseTimeToMs(this.config.system.collectInterval);if(this.collectInterval=setInterval(()=>{this.collect()},collectIntervalMs),this.config.persistence.enabled&&this.flushToDb){let flushIntervalMs=this.parseTimeToMs(this.config.persistence.flushInterval);this.flushInterval=setInterval(()=>{this.flush()},flushIntervalMs)}this.collect()}stop(){if(!this.isRunning)return;if(this.isRunning=!1,this.logger.info("[Monitoring] Stopping monitoring service"),this.collectInterval)clearInterval(this.collectInterval),this.collectInterval=null;if(this.flushInterval)clearInterval(this.flushInterval),this.flushInterval=null;this.flush()}async collect(){let now=Date.now(),snapshot={timestamp:now};if(this.config.system.enabled){let systemMetrics=await this.systemCollector.collect();if(systemMetrics)snapshot.system=systemMetrics,this.addMetricPoints("system",systemMetrics,now)}if(this.config.application.enabled){let appMetrics=this.applicationCollector.collect();if(appMetrics)snapshot.application=appMetrics,this.addMetricPoints("application",appMetrics,now)}if(this.databaseCollector){let dbMetrics=await this.databaseCollector.collect();if(dbMetrics)snapshot.database=dbMetrics,this.addMetricPoints("database",dbMetrics,now)}if(this.redisCollector){let redisMetrics=await this.redisCollector.collect();if(redisMetrics)snapshot.redis=redisMetrics,this.addMetricPoints("redis",redisMetrics,now)}if(await this.storeSnapshot(snapshot),this.config.alerts.enabled)await this.alertService.checkAndAlert(snapshot)}addMetricPoints(type,metrics,timestamp){let flatten=(obj,prefix="")=>{for(let key in obj){let value=obj[key],newKey=prefix?`${prefix}.${key}`:key;if(typeof value==="number")this.pendingMetrics.push({timestamp,metricType:type,metricName:newKey,value});else if(typeof value==="object"&&value!==null&&!Array.isArray(value))flatten(value,newKey)}};flatten(metrics)}async storeSnapshot(snapshot){let key=`monitoring:${this.appId}:latest`;await this.redis.create(key,snapshot,3600);let historyKey=`monitoring:${this.appId}:history`,historyResult=await this.redis.read(historyKey),history=historyResult.success&&historyResult.data?historyResult.data:[];history.push(snapshot);let oneHourAgo=Date.now()-3600000,filteredHistory=history.filter((s)=>s.timestamp>oneHourAgo);await this.redis.create(historyKey,filteredHistory,3600)}async flush(){if(this.pendingMetrics.length===0)return;if(!this.flushToDb)return;let metricsToFlush=[...this.pendingMetrics];this.pendingMetrics=[];try{await this.flushToDb(metricsToFlush),this.logger.debug(`[Monitoring] Flushed ${metricsToFlush.length} metrics to database`)}catch(error){this.logger.error(`[Monitoring] Failed to flush metrics: ${error}`),this.pendingMetrics=[...metricsToFlush,...this.pendingMetrics]}}recordRequest(params){if(!this.config.enabled||!this.config.application.enabled)return;this.applicationCollector.recordRequest(params)}recordRateLimitBlock(){if(!this.config.enabled||!this.config.application.enabled)return;this.applicationCollector.recordRateLimitBlock()}async getLatestSnapshot(){let key=`monitoring:${this.appId}:latest`,result=await this.redis.read(key);return result.success?result.data:null}async getHistory(minutes=60){let key=`monitoring:${this.appId}:history`,result=await this.redis.read(key);if(!result.success||!result.data)return[];let cutoff=Date.now()-minutes*60000;return result.data.filter((s)=>s.timestamp>cutoff)}getActiveAlerts(){return this.alertService.getActiveAlerts()}acknowledgeAlert(alertId){return this.alertService.acknowledgeAlert(alertId)}isEnabled(){return this.config.enabled}getConfig(){return this.config}}var DEFAULT_CONFIG3;var init_Monitoring=__esm(()=>{init_SystemCollector();init_LiveMonitoringService();DEFAULT_CONFIG3={enabled:!1,system:{enabled:!0,collectInterval:"10s",metrics:{cpu:!0,memory:!0,disk:!0,network:!1,process:!0}},application:{enabled:!0,metrics:{requests:!0,responseTime:!0,errors:!0,rateLimits:!0}},database:{enabled:!1,metrics:{connections:!0,queryTime:!0,slowQueryThreshold:"100ms"}},redis:{enabled:!1},persistence:{enabled:!0,flushInterval:"1m",retentionDays:30},alerts:{enabled:!1,email:{enabled:!1,recipients:[]},thresholds:{cpuPercent:80,memoryPercent:85,diskPercent:90,errorRatePercent:5,responseTimeMs:1000,rateLimitBlocksPerMinute:100},cooldown:"5m"}}});function snakeToCamel(name){return name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase())}function resolveSchemaTable(schemaTables,name,logger2){let direct=schemaTables[name];if(direct)return direct;let camel=snakeToCamel(name);if(camel!==name){let viaCamel=schemaTables[camel];if(viaCamel)return viaCamel}if(logger2){let keys=Object.keys(schemaTables),preview=keys.slice(0,20).join(", "),suffix=keys.length>20?`, ...(+${keys.length-20} more)`:"";logger2.warn(`[SchemaTables] Table "${name}" not found (tried "${name}" and "${camel}"). Known keys: ${preview}${suffix}`)}return}var init_types4=()=>{};import{and as and4,desc,eq as eq9}from"drizzle-orm";function resolveEnvValue2(value){if(!value)return;return process.env[value]??value}function toCamel(obj){let result={};for(let[key,value]of Object.entries(obj)){let camelKey=key.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());result[camelKey]=value}return result}function fromCamel(obj){let result={};for(let[key,value]of Object.entries(obj)){let snakeKey=key.replace(/[A-Z]/g,(c)=>`_${c.toLowerCase()}`);result[snakeKey]=value}return result}class NotificationService{db;schemaTables;config;logger;emailService;constructor(serviceConfig){this.db=serviceConfig.db,this.schemaTables=serviceConfig.schemaTables,this.config=serviceConfig.config,this.logger=serviceConfig.logger,this.emailService=serviceConfig.emailService}getTable(name){return resolveSchemaTable(this.schemaTables,name,this.logger)}getCol(table,col4){return table[col4]}isChannelEnabled(channel){let channels=this.config.channels;if(!channels)return channel==="portal";switch(channel){case"portal":return channels.portal!==!1;case"email":return channels.email===!0;case"telegram":return channels.telegram?.enabled===!0;case"webhook":return channels.webhook?.enabled===!0;default:return!1}}interpolateTemplate(template,context){let result=template;for(let[key,value]of Object.entries(context))result=result.replace(new RegExp(`{{${key}}}`,"g"),String(value??""));for(let[key,value]of Object.entries(this.config.templateVariables||{}))result=result.replace(new RegExp(`{{${key}}}`,"g"),value);return result}async triggerNotifications(params){let{trigger,flow_id,entity_name,entity_id,node_id,context={}}=params,rulesTable=this.getTable("verificationNotificationRules"),recipientsTable=this.getTable("verificationNotificationRecipients"),channelsTable=this.getTable("verificationNotificationChannels");if(!rulesTable||!recipientsTable){this.logger.warn("[Notification] Notification tables not found");return}let now=new Date,rules=await this.db.select().from(rulesTable).where(and4(eq9(this.getCol(rulesTable,"flowId"),flow_id),eq9(this.getCol(rulesTable,"trigger"),trigger)));this.logger.info(`[Notification] Found ${rules.length} rules for trigger=${trigger} flow_id=${flow_id}, filter_node_id=${node_id||"NONE"}`);for(let r of rules)this.logger.info(`[Notification] Rule ${r.id}: nodeId=${r.nodeId}, trigger=${r.trigger}, title=${JSON.stringify(r.titleTemplate)}`);let filteredRules=rules.filter((rule)=>{if(node_id&&rule.nodeId!==node_id)return this.logger.info(`[Notification] EXCLUDED rule ${rule.id}: rule.nodeId=${rule.nodeId} !== filter_node_id=${node_id}`),!1;if(rule.startsAt&&new Date(rule.startsAt)>now)return!1;if(rule.expiresAt&&new Date(rule.expiresAt)<now)return!1;return!0});for(let rule of filteredRules){let recipients=await this.db.select().from(recipientsTable).where(eq9(this.getCol(recipientsTable,"ruleId"),rule.id)),ruleChannels=["portal"];if(channelsTable){let channelEntries=await this.db.select().from(channelsTable).where(eq9(this.getCol(channelsTable,"ruleId"),rule.id));if(channelEntries.length>0)ruleChannels=channelEntries.map((c)=>c.channel)}let enabledChannels=ruleChannels.filter((ch)=>this.isChannelEnabled(ch));if(enabledChannels.length===0)continue;this.logger.info(`[Notification] Rule ${rule.id}: ${recipients.length} recipients, ${enabledChannels.length} channels (${enabledChannels.join(",")})`);let userIds=await this.resolveRecipients(recipients,params.verifier_id,flow_id,entity_name,entity_id);this.logger.info(`[Notification] Rule ${rule.id}: resolved ${userIds.length} user IDs: ${userIds.join(", ")}`);let enrichedContext={...context,entity_name,entity_id,trigger,decision:params.decision};this.logger.info(`[Notification] Rule ${rule.id}: titleTemplate=${JSON.stringify(rule.titleTemplate)}, bodyTemplate=${JSON.stringify(rule.bodyTemplate)}, context=${JSON.stringify(enrichedContext)}`);let title=rule.titleTemplate?this.interpolateTemplate(rule.titleTemplate,enrichedContext):`Verification ${trigger.replace("on_","").replace("_"," ")}`,body=rule.bodyTemplate?this.interpolateTemplate(rule.bodyTemplate,enrichedContext):void 0;this.logger.info(`[Notification] Rule ${rule.id}: final title="${title}", body="${body}"`);for(let userId of userIds)await this.send({user_id:userId,title,body,entity_name,entity_id,type:"verification",source:`flow:${flow_id}`,channels:enabledChannels})}this.logger.debug(`[Notification] Triggered ${filteredRules.length} rules for ${trigger} on ${entity_name}:${entity_id}`)}async resolveRecipients(recipients,currentVerifierId,flowId,entityName,entityId){let userIds=new Set,userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");for(let recipient of recipients)switch(recipient.recipientType){case"user":if(recipient.recipientUserId)userIds.add(recipient.recipientUserId);break;case"role":if(recipient.recipientRole&&userRolesTable&&rolesTable){let rolesCols=rolesTable,userRolesCols=userRolesTable,role=(await this.db.select().from(rolesTable).where(eq9(rolesCols.name,recipient.recipientRole)).limit(1))[0];if(role){let usersInRole=await this.db.select({user_id:userRolesCols.userId}).from(userRolesTable).where(eq9(userRolesCols.roleId,role.id));for(let ur of usersInRole)userIds.add(ur.user_id)}}break;case"step_verifier":if(currentVerifierId)userIds.add(currentVerifierId);break;case"entity_creator":{if(entityName&&entityId){let instancesTable=this.getTable("verificationInstances");if(instancesTable){let inst=(await this.db.select().from(instancesTable).where(and4(eq9(this.getCol(instancesTable,"entityName"),entityName),eq9(this.getCol(instancesTable,"entityId"),entityId))).orderBy(desc(this.getCol(instancesTable,"createdAt"))).limit(1))[0];if(inst?.startedBy)userIds.add(inst.startedBy)}}break}case"all_verifiers":{if(flowId){let verifierConfigsTable=this.getTable("verificationVerifierConfigs");if(verifierConfigsTable){let configs=await this.db.select().from(verifierConfigsTable).where(eq9(this.getCol(verifierConfigsTable,"flowId"),flowId));this.logger.info(`[Notification] all_verifiers: found ${configs.length} verifier configs for flow ${flowId}`);for(let cfg of configs){let row=cfg;if(this.logger.info(`[Notification] all_verifiers: config node_id=${row.nodeId}, type=${row.verifierType}, userId=${row.verifierUserId}, role=${row.verifierRole}`),row.verifierUserId)userIds.add(row.verifierUserId);if(row.verifierType==="role"&&row.verifierRole&&userRolesTable&&rolesTable){let rCols=rolesTable,urCols=userRolesTable,roleRow=(await this.db.select().from(rolesTable).where(eq9(rCols.name,row.verifierRole)).limit(1))[0];if(roleRow){let usersInRole=await this.db.select({user_id:urCols.userId}).from(userRolesTable).where(eq9(urCols.roleId,roleRow.id));for(let ur of usersInRole)userIds.add(ur.user_id)}}}}}break}}return Array.from(userIds)}async send(params){let{user_id,title,body,entity_name,entity_id,type,source,channels}=params;for(let channel of channels)switch(channel){case"portal":await this.sendPortalNotification(user_id,title,body,entity_name,entity_id,type,source);break;case"email":await this.sendEmailNotification(user_id,title,body);break;case"sms":this.logger.warn(`[Notification] SMS delivery is not supported \u2014 skipping sms notification for user ${user_id}`);break;case"telegram":await this.sendTelegramNotification(params);break;case"webhook":await this.sendWebhookNotification(params);break}}async sendPortalNotification(userId,title,body,entityName,entityId,type,source){let notificationsTable=this.getTable("notifications");if(!notificationsTable){this.logger.warn("[Notification] notifications table not found");return}await this.db.insert(notificationsTable).values(toCamel({user_id:userId,title,body:body||null,entity_name:entityName||null,entity_id:entityId||null,type:type||"system",source:source||null,is_seen:!1})),this.logger.debug(`[Notification] Portal notification sent to ${userId}: ${title}`)}async sendTelegramNotification(params){let telegramConfig=this.config.channels?.telegram,botToken=resolveEnvValue2(telegramConfig?.botToken),chatId=resolveEnvValue2(telegramConfig?.chatId);if(!botToken||!chatId){this.logger.warn("[Notification] Telegram channel enabled but botToken/chatId is missing or unresolved \u2014 skipping delivery");return}let text=params.body?`*${params.title}*
88
88
 
89
- ${params.body}`:`*${params.title}*`,timeoutMs=telegramConfig?.timeoutMs??DEFAULT_DELIVERY_TIMEOUT_MS;try{let response=await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:chatId,text,parse_mode:"Markdown"}),signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){let errBody=await response.text().catch(()=>"");this.logger.error(`[Notification] Telegram delivery failed (${response.status}): ${errBody.slice(0,300)}`);return}this.logger.debug(`[Notification] Telegram notification sent: ${params.title}`)}catch(error){this.logger.error(`[Notification] Telegram delivery error: ${error.message}`)}}async sendWebhookNotification(params){let webhookConfig=this.config.channels?.webhook,url=resolveEnvValue2(webhookConfig?.url);if(!url){this.logger.warn("[Notification] Webhook channel enabled but url is missing or unresolved \u2014 skipping delivery");return}let headers={"Content-Type":"application/json"};for(let[key,value]of Object.entries(webhookConfig?.headers||{})){let resolved=resolveEnvValue2(value);if(resolved)headers[key]=resolved}let timeoutMs=webhookConfig?.timeoutMs??DEFAULT_DELIVERY_TIMEOUT_MS;try{let response=await fetch(url,{method:"POST",headers,body:JSON.stringify({user_id:params.user_id,title:params.title,body:params.body??null,entity_name:params.entity_name??null,entity_id:params.entity_id??null,type:params.type??"system",source:params.source??null,timestamp:new Date().toISOString()}),signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){let errBody=await response.text().catch(()=>"");this.logger.error(`[Notification] Webhook delivery failed (${response.status}): ${errBody.slice(0,300)}`);return}this.logger.debug(`[Notification] Webhook notification sent: ${params.title}`)}catch(error){this.logger.error(`[Notification] Webhook delivery error: ${error.message}`)}}async sendEmailNotification(userId,title,body){if(!this.emailService?.isAvailable()){this.logger.warn("[Notification] Email service not available for email notification");return}let usersTable=this.getTable("users");if(!usersTable){this.logger.warn("[Notification] users table not found");return}let user=(await this.db.select({email:this.getCol(usersTable,"email")}).from(usersTable).where(eq9(this.getCol(usersTable,"id"),userId)).limit(1))[0];if(!user?.email){this.logger.warn(`[Notification] No email found for user ${userId}`);return}await this.emailService.sendEmail({to:user.email,subject:title,html:body||title}),this.logger.debug(`[Notification] Email notification sent to ${user.email}: ${title}`)}async getNotifications(userId,options){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return[];let conditions=[eq9(this.getCol(notificationsTable,"userId"),userId)];if(options?.type)conditions.push(eq9(this.getCol(notificationsTable,"type"),options.type));return(await this.db.select().from(notificationsTable).where(and4(...conditions)).orderBy(desc(this.getCol(notificationsTable,"createdAt"))).limit(options?.limit||50).offset(options?.offset||0)).map((r)=>fromCamel(r))}async getUnseenCount(userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return 0;return(await this.db.select().from(notificationsTable).where(and4(eq9(this.getCol(notificationsTable,"userId"),userId),eq9(this.getCol(notificationsTable,"isSeen"),!1)))).length}async markAsSeen(notificationId,userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return!1;return await this.db.update(notificationsTable).set(toCamel({is_seen:!0,seen_at:new Date})).where(and4(eq9(this.getCol(notificationsTable,"id"),notificationId),eq9(this.getCol(notificationsTable,"userId"),userId))),!0}async markAllAsSeen(userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return 0;return(await this.db.update(notificationsTable).set(toCamel({is_seen:!0,seen_at:new Date})).where(and4(eq9(this.getCol(notificationsTable,"userId"),userId),eq9(this.getCol(notificationsTable,"isSeen"),!1))).returning()).length}}var DEFAULT_DELIVERY_TIMEOUT_MS=1e4;var init_Notification=__esm(()=>{init_types4()});function buildGenericAuthUrl(config,state){if(!config.authorizationUrl)throw Error("Generic OAuth provider requires authorizationUrl");let scopes2=config.scopes??[],params=new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",state,...scopes2.length>0?{scope:scopes2.join(" ")}:{},...config.extraAuthParams});return`${config.authorizationUrl}?${params.toString()}`}async function exchangeGenericCode(code,config){if(!config.tokenUrl)throw Error("Generic OAuth provider requires tokenUrl");let tokenRes=await fetch(config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`Generic OAuth token exchange failed: ${err}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`OAuth error: ${tokenData.error_description||tokenData.error}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope};if(!config.userInfoUrl)return{profile:{providerAccountId:tokenData.access_token,rawProfile:tokenData},tokens};let userRes=await fetch(config.userInfoUrl,{headers:{Authorization:`Bearer ${tokenData.access_token}`}});if(!userRes.ok)throw Error("Failed to fetch user info from generic OAuth provider");let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.id??rawProfile.sub??rawProfile.user_id??tokenData.access_token,email:rawProfile.email,emailVerified:rawProfile.email_verified===!0||rawProfile.email_verified==="true",name:rawProfile.name??rawProfile.display_name??rawProfile.username,avatarUrl:rawProfile.avatar_url??rawProfile.picture??rawProfile.photo,rawProfile},tokens}}function buildGithubAuthUrl(config,state){let scopes2=config.scopes??["read:user","user:email"];return`https://github.com/login/oauth/authorize?${new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,scope:scopes2.join(" "),state,...config.extraAuthParams}).toString()}`}async function exchangeGithubCode(code,config){let tokenRes=await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`GitHub token exchange failed: ${err}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`GitHub OAuth error: ${tokenData.error_description||tokenData.error}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes=await fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${tokenData.access_token}`,Accept:"application/vnd.github+json"}});if(!userRes.ok)throw Error("Failed to fetch GitHub user info");let rawProfile=await userRes.json(),email=rawProfile.email,emailVerified=!!email;if(!email)try{let emailsRes=await fetch("https://api.github.com/user/emails",{headers:{Authorization:`Bearer ${tokenData.access_token}`,Accept:"application/vnd.github+json"}});if(emailsRes.ok){let emails=await emailsRes.json(),primary=emails.find((e)=>e.primary&&e.verified);if(primary)email=primary.email,emailVerified=!0;else email=emails[0]?.email,emailVerified=!1}}catch{}return{profile:{providerAccountId:String(rawProfile.id),email,emailVerified,name:rawProfile.name??rawProfile.login,avatarUrl:rawProfile.avatar_url,rawProfile},tokens}}function buildGoogleAuthUrl(config,state){let scopes2=config.scopes??["openid","email","profile"];return`https://accounts.google.com/o/oauth2/v2/auth?${new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",scope:scopes2.join(" "),state,access_type:"offline",prompt:"select_account",...config.extraAuthParams}).toString()}`}async function exchangeGoogleCode(code,config){let tokenRes=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`Google token exchange failed: ${err}`)}let tokenData=await tokenRes.json(),tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes=await fetch("https://www.googleapis.com/oauth2/v3/userinfo",{headers:{Authorization:`Bearer ${tokenData.access_token}`}});if(!userRes.ok)throw Error("Failed to fetch Google user info");let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.sub,email:rawProfile.email,emailVerified:rawProfile.email_verified===!0||rawProfile.email_verified==="true",name:rawProfile.name,avatarUrl:rawProfile.picture,rawProfile},tokens}}function getMicrosoftBaseUrl(config){return`https://login.microsoftonline.com/${config.tenantId??"common"}/oauth2/v2.0`}function describeFetchError(err){if(err&&typeof err==="object"){let e=err,cause=e.cause,parts=[];if(e.message)parts.push(`message=${e.message}`);if(e.code)parts.push(`code=${e.code}`);if(cause?.code)parts.push(`cause.code=${cause.code}`);if(cause?.name)parts.push(`cause.name=${cause.name}`);if(cause?.message)parts.push(`cause.message=${cause.message}`);return parts.join(" | ")||String(err)}return String(err)}function buildMicrosoftAuthUrl(config,state){let scopes2=config.scopes??["openid","email","profile","User.Read"],params=new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",scope:scopes2.join(" "),state,response_mode:"query",prompt:"select_account",...config.extraAuthParams});return`${getMicrosoftBaseUrl(config)}/authorize?${params.toString()}`}async function exchangeMicrosoftCode(code,config){let tokenUrl=`${getMicrosoftBaseUrl(config)}/token`,tokenRes;try{tokenRes=await fetch(tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()})}catch(err){throw Error(`Microsoft token endpoint fetch failed | url=${tokenUrl} | redirect_uri=${config.redirectUri} | ${describeFetchError(err)}`,{cause:err})}if(!tokenRes.ok){let body=await tokenRes.text().catch(()=>"<unable to read body>");throw Error(`Microsoft token exchange failed | status=${tokenRes.status} | url=${tokenUrl} | redirect_uri=${config.redirectUri} | body=${body}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`Microsoft OAuth error: ${tokenData.error_description||tokenData.error} | url=${tokenUrl}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes;try{userRes=await fetch("https://graph.microsoft.com/v1.0/me",{headers:{Authorization:`Bearer ${tokenData.access_token}`}})}catch(err){throw Error(`Microsoft userinfo fetch failed | url=https://graph.microsoft.com/v1.0/me | ${describeFetchError(err)}`,{cause:err})}if(!userRes.ok){let body=await userRes.text().catch(()=>"<unable to read body>");throw Error(`Microsoft userinfo failed | status=${userRes.status} | url=https://graph.microsoft.com/v1.0/me | body=${body}`)}let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.id,email:rawProfile.mail??rawProfile.userPrincipalName,emailVerified:!0,name:rawProfile.displayName,avatarUrl:void 0,rawProfile},tokens}}var exports_stateStore={};__export(exports_stateStore,{createRedisOAuthStateStore:()=>createRedisOAuthStateStore,InMemoryOAuthStateStore:()=>InMemoryOAuthStateStore});class InMemoryOAuthStateStore{store=new Map;cleanupInterval=null;constructor(cleanupEverySeconds){this.cleanupInterval=setInterval(()=>{let now=Date.now();for(let[k,v]of this.store.entries())if(v.expiresAt<now)this.store.delete(k)},cleanupEverySeconds*1000),this.cleanupInterval.unref?.()}async set(state,payload,ttlSeconds){this.store.set(state,{payload,expiresAt:Date.now()+ttlSeconds*1000})}async consume(state){let entry=this.store.get(state);if(!entry)return null;if(this.store.delete(state),entry.expiresAt<Date.now())return null;return entry.payload}stop(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}}var key=(state)=>`nucleus:oauth:state:${state}`,createRedisOAuthStateStore=(redis)=>({async set(state,payload,ttlSeconds){await redis.create(key(state),JSON.stringify(payload),ttlSeconds)},async consume(state){let res=await redis.read(key(state));if(!res.success||!res.data)return null;await redis.remove(key(state));try{return JSON.parse(res.data)}catch{return null}}});import{randomBytes as randomBytes3}from"crypto";class OAuthService{config;stateStore;constructor(config,stateStore){this.config=config,this.stateStore=stateStore??new InMemoryOAuthStateStore(config.stateTtlSeconds??600)}stop(){this.stateStore.stop?.()}isProviderEnabled(provider){return!!(this.config.enabled&&this.config.providers[provider])}getEnabledProviders(){if(!this.config.enabled)return[];return Object.keys(this.config.providers)}async buildAuthorizationUrl(provider,linkUserId,redirectUrl){let providerConfig=this.config.providers[provider];if(!providerConfig)throw Error(`OAuth provider "${provider}" is not configured`);let statePayload={provider,linkUserId,redirectUrl,createdAt:Date.now()},state=randomBytes3(32).toString("hex"),ttlSeconds=this.config.stateTtlSeconds??600;switch(await this.stateStore.set(state,statePayload,ttlSeconds),provider){case"google":return buildGoogleAuthUrl(providerConfig,state);case"github":return buildGithubAuthUrl(providerConfig,state);case"microsoft":return buildMicrosoftAuthUrl(providerConfig,state);default:return buildGenericAuthUrl(providerConfig,state)}}consumeState(state){return this.stateStore.consume(state)}async exchangeCode(provider,code){let providerConfig=this.config.providers[provider];if(!providerConfig)throw Error(`OAuth provider "${provider}" is not configured`);switch(provider){case"google":return exchangeGoogleCode(code,providerConfig);case"github":return exchangeGithubCode(code,providerConfig);case"microsoft":return exchangeMicrosoftCode(code,providerConfig);default:return exchangeGenericCode(code,providerConfig)}}get allowAccountLinking(){return this.config.allowAccountLinking??!0}get autoCreateUser(){return this.config.autoCreateUser??!0}get successRedirectUrl(){return this.config.successRedirectUrl??"/"}get errorRedirectUrl(){return this.config.errorRedirectUrl??"/login"}get sendInviteOnCreate(){return this.config.sendInviteOnCreate??!1}get basePath(){return this.config.basePath??"/auth/oauth"}}var init_OAuthService=()=>{};var init_OAuth=__esm(()=>{init_OAuthService()});import*as crypto5 from"crypto";var IYZICO_PATHS,generateAuthString=(apiKey,secretKey,path2,payload,randomKey)=>{let compound=payload?randomKey+path2+payload:randomKey+path2,encryptedData=crypto5.createHmac("sha256",secretKey).update(compound).digest("hex"),authorizationString=`apiKey:${apiKey}&randomKey:${randomKey}&signature:${encryptedData}`;return`IYZWSv2 ${Buffer.from(authorizationString).toString("base64")}`},buildRequest=(config,path2,payload)=>{let body=JSON.stringify(payload),randomKey=Date.now().toString(32),headers=new Headers;return headers.set("Content-Type","application/json"),headers.set("x-iyzi-rnd",randomKey),headers.set("Authorization",generateAuthString(config.apiKey,config.secretKey,path2,body,randomKey)),{url:new URL(`${config.apiUrl}${path2}`).toString(),headers,body}},iyzicoFetch=async(config,path2,payload)=>{let{url,headers,body}=buildRequest(config,path2,payload);try{let json=await(await fetch(url,{method:"POST",headers,body})).json(),isSuccess=json.status==="success";return{success:isSuccess,data:isSuccess?json:null,errorCode:json.errorCode??void 0,errorMessage:json.errorMessage??void 0,rawResponse:json}}catch(error){let message=error instanceof Error?error.message:"Unknown fetch error";return{success:!1,data:null,errorCode:"NETWORK_ERROR",errorMessage:message,rawResponse:{error:message}}}},createIyzicoProvider=(config)=>{let generateConversationId=()=>`nk-${Date.now().toString(16)}`;return{get name(){return"iyzico"},async initialize3DS(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId,price:request.price,paidPrice:request.paidPrice,currency:request.currency,installment:request.installment??1,paymentChannel:request.paymentChannel??"WEB",basketId:request.basketId??Date.now().toString(16),paymentGroup:request.paymentGroup??"PRODUCT",paymentCard:request.paymentCard,buyer:request.buyer,shippingAddress:request.shippingAddress,billingAddress:request.billingAddress,basketItems:request.basketItems,callbackUrl:request.callbackUrl},result=await iyzicoFetch(config,IYZICO_PATHS.INITIALIZE_3DS,payload);return{success:result.success,threeDSHtmlContent:result.data?.threeDSHtmlContent,paymentId:result.data?.paymentId,signature:result.data?.signature,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async complete3DS(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId,paymentId:request.paymentId},result=await iyzicoFetch(config,IYZICO_PATHS.COMPLETE_3DS,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,paidPrice:result.data?.paidPrice,currency:result.data?.currency,installment:result.data?.installment,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,binNumber:result.data?.binNumber,lastFourDigits:result.data?.lastFourDigits,basketId:result.data?.basketId,authCode:result.data?.authCode,signature:result.data?.signature,fraudStatus:result.data?.fraudStatus,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async getPaymentDetail(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),paymentId:request.paymentId,paymentConversationId:request.paymentConversationId},result=await iyzicoFetch(config,IYZICO_PATHS.PAYMENT_DETAIL,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,paidPrice:result.data?.paidPrice,currency:result.data?.currency,basketId:result.data?.basketId,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,lastFourDigits:result.data?.lastFourDigits,binNumber:result.data?.binNumber,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async queryBin(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),binNumber:request.binNumber.slice(0,6)};if(request.price!==void 0)payload.price=request.price;let result=await iyzicoFetch(config,IYZICO_PATHS.BIN_CHECK,payload);return{success:result.success,binNumber:result.data?.binNumber,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,bankName:result.data?.bankName,bankCode:result.data?.bankCode,commercial:result.data?.commercial,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async saveCard(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),card:request.card,email:request.email,externalId:request.externalId},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_SAVE,payload);return{success:result.success,cardUserKey:result.data?.cardUserKey,cardToken:result.data?.cardToken,cardAlias:result.data?.cardAlias,binNumber:result.data?.binNumber,lastFourDigits:result.data?.lastFourDigits,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,cardBankName:result.data?.cardBankName,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async listCards(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),cardUserKey:request.cardUserKey},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_LIST,payload),cards=(result.data?.cardDetails??[]).map((cd)=>({cardToken:cd.cardToken,cardAlias:cd.cardAlias,binNumber:cd.binNumber,lastFourDigits:cd.lastFourDigits,cardType:cd.cardType,cardAssociation:cd.cardAssociation,cardFamily:cd.cardFamily,cardBankName:cd.cardBankName,cardBankCode:cd.cardBankCode}));return{success:result.success,cardUserKey:result.data?.cardUserKey,cards,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async deleteCard(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),cardUserKey:request.cardUserKey,cardToken:request.cardToken},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_DELETE,payload);return{success:result.success,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async refund(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),paymentTransactionId:request.paymentTransactionId,price:request.price,currency:request.currency??"TRY"};if(request.ip)payload.ip=request.ip;let result=await iyzicoFetch(config,IYZICO_PATHS.REFUND,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,currency:result.data?.currency,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},parseCallback(formData){return{success:formData.mdStatus==="1",paymentId:formData.paymentId??"",conversationId:formData.conversationId??"",mdStatus:formData.mdStatus??"",rawData:formData}},async registerSubMerchant(request){let typeMap={personal:"PERSONAL",private_company:"PRIVATE_COMPANY",limited_or_joint_stock_company:"LIMITED_OR_JOINT_STOCK_COMPANY"},payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantExternalId:request.subMerchantExternalId,subMerchantType:typeMap[request.subMerchantType]??"PERSONAL",name:request.name,email:request.email,gsmNumber:request.gsmNumber,address:request.address,iban:request.iban,contactName:request.contactName,contactSurname:request.contactSurname,identityNumber:request.identityNumber,currency:request.currency??"TRY"};if(request.subMerchantType==="private_company")payload.taxOffice=request.taxOffice,payload.legalCompanyTitle=request.legalCompanyTitle;if(request.subMerchantType==="limited_or_joint_stock_company")payload.taxOffice=request.taxOffice,payload.taxNumber=request.taxNumber,payload.legalCompanyTitle=request.legalCompanyTitle;let result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_CREATE,payload);return{success:result.success,subMerchantKey:result.data?.subMerchantKey,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async updateSubMerchant(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantKey:request.subMerchantKey};if(request.name)payload.name=request.name;if(request.email)payload.email=request.email;if(request.gsmNumber)payload.gsmNumber=request.gsmNumber;if(request.address)payload.address=request.address;if(request.iban)payload.iban=request.iban;if(request.contactName)payload.contactName=request.contactName;if(request.contactSurname)payload.contactSurname=request.contactSurname;if(request.identityNumber)payload.identityNumber=request.identityNumber;if(request.taxOffice)payload.taxOffice=request.taxOffice;if(request.taxNumber)payload.taxNumber=request.taxNumber;if(request.legalCompanyTitle)payload.legalCompanyTitle=request.legalCompanyTitle;if(request.currency)payload.currency=request.currency;let result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_UPDATE,payload);return{success:result.success,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async getSubMerchant(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantExternalId:request.subMerchantExternalId},result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_DETAIL,payload);return{success:result.success,subMerchantKey:result.data?.subMerchantKey,name:result.data?.name,email:result.data?.email,gsmNumber:result.data?.gsmNumber,address:result.data?.address,iban:result.data?.iban,subMerchantType:result.data?.subMerchantType,currency:result.data?.currency,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async approveSplit(request){let payload={locale:request.locale??"tr",paymentTransactionId:request.paymentTransactionId},result=await iyzicoFetch(config,IYZICO_PATHS.APPROVAL,payload);return{success:result.success,paymentTransactionId:result.data?.paymentTransactionId,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async disapproveSplit(request){let payload={locale:request.locale??"tr",paymentTransactionId:request.paymentTransactionId},result=await iyzicoFetch(config,IYZICO_PATHS.DISAPPROVAL,payload);return{success:result.success,paymentTransactionId:result.data?.paymentTransactionId,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async createProduct(request){let localId=`iyzico-prod-${Date.now().toString(36)}`;return{success:!0,providerProductId:localId,name:request.name,rawResponse:{local:!0,id:localId}}},async updateProduct(request){return{success:!0,providerProductId:request.providerProductId,rawResponse:{local:!0,updated:request.providerProductId}}},async getProduct(request){return{success:!0,providerProductId:request.providerProductId,rawResponse:{local:!0,message:"Product data is stored locally for iyzico"}}},async listProducts(_request){return{success:!0,products:[],hasMore:!1,rawResponse:{local:!0,message:"Products are stored locally for iyzico"}}},async archiveProduct(request){return{success:!0,rawResponse:{local:!0,archived:request.providerProductId}}},async createPrice(request){let localId=`iyzico-price-${Date.now().toString(36)}`;return{success:!0,providerPriceId:localId,rawResponse:{local:!0,id:localId,product:request.providerProductId}}},async updatePrice(request){return{success:!0,providerPriceId:request.providerPriceId,rawResponse:{local:!0,updated:request.providerPriceId}}},async getPrice(request){return{success:!0,providerPriceId:request.providerPriceId,rawResponse:{local:!0,message:"Price data is stored locally for iyzico"}}},async listPrices(_request){return{success:!0,prices:[],hasMore:!1,rawResponse:{local:!0,message:"Prices are stored locally for iyzico"}}},async archivePrice(request){return{success:!0,rawResponse:{local:!0,archived:request.providerPriceId}}},async createCustomer(request){return{success:!0,providerCustomerId:`local_cus_${Date.now()}`,email:request.email,rawResponse:{local:!0,message:"Customer stored locally for iyzico"}}},async updateCustomer(request){return{success:!0,providerCustomerId:request.providerCustomerId,rawResponse:{local:!0,message:"Customer updated locally for iyzico"}}},async getCustomer(request){return{success:!0,providerCustomerId:request.providerCustomerId,rawResponse:{local:!0,message:"Customer data is stored locally for iyzico"}}},async listCustomers(_request){return{success:!0,customers:[],hasMore:!1,rawResponse:{local:!0,message:"Customers are stored locally for iyzico"}}},async deleteCustomer(_request){return{success:!0,rawResponse:{local:!0,message:"Customer deleted locally for iyzico"}}},async createSubscription(_request){return{success:!0,providerSubscriptionId:`local_sub_${Date.now()}`,status:"active",rawResponse:{local:!0,message:"Subscription stored locally for iyzico"}}},async updateSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,status:"active",rawResponse:{local:!0,message:"Subscription updated locally for iyzico"}}},async cancelSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,status:"canceled",rawResponse:{local:!0,message:"Subscription canceled locally for iyzico"}}},async getSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,rawResponse:{local:!0,message:"Subscription data is stored locally for iyzico"}}},async listSubscriptions(_request){return{success:!0,subscriptions:[],hasMore:!1,rawResponse:{local:!0,message:"Subscriptions are stored locally for iyzico"}}},async reportUsage(request){return{success:!0,usageRecordId:`local_ur_${Date.now()}`,quantity:request.quantity,timestamp:request.timestamp??Math.floor(Date.now()/1000),rawResponse:{local:!0,message:"Usage recorded locally for iyzico"}}},async listUsageRecordSummaries(_request){return{success:!0,summaries:[],hasMore:!1,rawResponse:{local:!0,message:"Usage summaries are stored locally for iyzico"}}},async createInvoice(_request){return{success:!0,providerInvoiceId:`local_inv_${Date.now()}`,status:"draft",rawResponse:{local:!0,message:"Invoice stored locally for iyzico"}}},async finalizeInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,status:"open",rawResponse:{local:!0,message:"Invoice finalized locally for iyzico"}}},async sendInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice send is a no-op for iyzico"}}},async voidInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice voided locally for iyzico"}}},async payInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,status:"paid",rawResponse:{local:!0,message:"Invoice payment is a no-op for iyzico"}}},async getInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice data is stored locally for iyzico"}}},async listInvoices(_request){return{success:!0,invoices:[],hasMore:!1,rawResponse:{local:!0,message:"Invoices are stored locally for iyzico"}}},async getBalance(){return{success:!0,available:[],pending:[],rawResponse:{local:!0,message:"Balance is not tracked by iyzico provider"}}},async createPayout(_request){return{success:!0,providerPayoutId:`local_po_${Date.now()}`,status:"pending",rawResponse:{local:!0,message:"Payout created locally for iyzico"}}},async getPayout(request){return{success:!0,providerPayoutId:request.providerPayoutId,rawResponse:{local:!0,message:"Payout data is stored locally for iyzico"}}},async cancelPayout(request){return{success:!0,providerPayoutId:request.providerPayoutId,status:"canceled",rawResponse:{local:!0,message:"Payout cancelled locally for iyzico"}}},async listPayouts(_request){return{success:!0,payouts:[],hasMore:!1,rawResponse:{local:!0,message:"Payouts are stored locally for iyzico"}}},async createTransfer(_request){return{success:!0,providerTransferId:`local_tr_${Date.now()}`,rawResponse:{local:!0,message:"Transfer created locally for iyzico"}}},async listTransfers(_request){return{success:!0,transfers:[],hasMore:!1,rawResponse:{local:!0,message:"Transfers are stored locally for iyzico"}}}}};var init_IyzicoProvider=__esm(()=>{IYZICO_PATHS={INITIALIZE_3DS:"/payment/3dsecure/initialize",COMPLETE_3DS:"/payment/3dsecure/auth",PAYMENT_AUTH:"/payment/auth",PAYMENT_DETAIL:"/payment/detail",BIN_CHECK:"/payment/bin/check",CARD_SAVE:"/cardstorage/card",CARD_LIST:"/cardstorage/cards",CARD_DELETE:"/cardstorage/card/delete",REFUND:"/payment/refund",SUB_MERCHANT_CREATE:"/onboarding/submerchant",SUB_MERCHANT_UPDATE:"/onboarding/submerchant",SUB_MERCHANT_DETAIL:"/onboarding/submerchant/detail",APPROVAL:"/payment/iyzipos/item/approve",DISAPPROVAL:"/payment/iyzipos/item/disapprove"}});var resolveMarketplaceConfig=(payment)=>{let m=payment?.marketplace;return{enabled:m?.enabled===!0,defaultSettlementDelayDays:m?.defaultSettlementDelayDays??7,newSellerDelayDays:m?.newSellerDelayDays??14,reserveRate:clampRate(m?.reserveRate??0),reserveReleaseDays:m?.reserveReleaseDays??0,minimumPayoutAmount:m?.minimumPayoutAmount??0,defaultCurrency:m?.defaultCurrency??payment?.defaultCurrency??"USD",autoReleaseReserves:m?.autoReleaseReserves===!0}},clampRate=(rate)=>{if(Number.isNaN(rate)||rate<0)return 0;if(rate>1)return 1;return rate},addDays=(base,days)=>new Date(base.getTime()+days*24*60*60*1000),computeSplitBreakdown=(input,config,policy,now=new Date)=>{let providerFee=Math.max(0,Math.round(input.providerFeeAmount??0)),platformFee=Math.max(0,Math.round(input.platformFeeAmount)),afterFees=Math.max(0,input.grossAmount-providerFee-platformFee),reserveRate=input.reserveRate??policy?.reserveRate??config.reserveRate,reserveAmount=Math.min(afterFees,Math.floor(afterFees*clampRate(reserveRate))),netPayable=afterFees-reserveAmount,delayDays=input.settlementDelayDays??(input.isNewSeller?policy?.newSellerDelayDays??config.newSellerDelayDays:policy?.defaultDelayDays??config.defaultSettlementDelayDays);return{grossAmount:input.grossAmount,providerFeeAmount:providerFee,platformFeeAmount:platformFee,reserveAmount,netPayableAmount:netPayable,availableForPayoutAt:addDays(now,Math.max(0,delayDays))}},str2=(v)=>v==null?"":String(v),strOrNull2=(v)=>v==null?null:String(v),num=(v)=>{let n=typeof v==="number"?v:Number(v);return Number.isFinite(n)?n:0},rowToSplit=(row)=>({id:str2(row.id),transactionId:strOrNull2(row.transactionId??row.transaction_id),recipientOwnerType:str2(row.recipientOwnerType??row.recipient_owner_type)||"seller",recipientOwnerId:str2(row.recipientOwnerId??row.recipient_owner_id),provider:str2(row.provider),grossAmount:num(row.grossAmount??row.gross_amount),providerFeeAmount:num(row.providerFeeAmount??row.provider_fee_amount),platformFeeAmount:num(row.platformFeeAmount??row.platform_fee_amount),reserveAmount:num(row.reserveAmount??row.reserve_amount),netPayableAmount:num(row.netPayableAmount??row.net_payable_amount),currency:str2(row.currency),availableForPayoutAt:strOrNull2(row.availableForPayoutAt??row.available_for_payout_at),status:str2(row.status)||"pending",sourceType:strOrNull2(row.sourceType??row.source_type),sourceId:strOrNull2(row.sourceId??row.source_id)}),rowToPayoutRequest=(row)=>({id:str2(row.id),recipientOwnerType:str2(row.recipientOwnerType??row.recipient_owner_type)||"seller",recipientOwnerId:str2(row.recipientOwnerId??row.recipient_owner_id),amount:num(row.amount),currency:str2(row.currency),status:str2(row.status)||"pending",provider:strOrNull2(row.provider),providerPayoutId:strOrNull2(row.providerPayoutId??row.provider_payout_id),providerTransferId:strOrNull2(row.providerTransferId??row.provider_transfer_id),destination:strOrNull2(row.destination),failureReason:strOrNull2(row.failureReason??row.failure_reason),requestedBy:strOrNull2(row.requestedBy??row.requested_by)}),rowToSettlementPolicy=(row)=>({id:str2(row.id),ownerType:str2(row.ownerType??row.owner_type),ownerId:str2(row.ownerId??row.owner_id),defaultDelayDays:num(row.defaultDelayDays??row.default_delay_days),newSellerDelayDays:num(row.newSellerDelayDays??row.new_seller_delay_days),reserveRate:num(row.reserveRate??row.reserve_rate),reserveDays:num(row.reserveDays??row.reserve_days),minimumPayoutAmount:num(row.minimumPayoutAmount??row.minimum_payout_amount),currency:strOrNull2(row.currency),earlyPayoutEnabled:Boolean(row.earlyPayoutEnabled??row.early_payout_enabled),status:str2(row.status)||"active"}),rowToDispute=(row)=>({id:str2(row.id),transactionId:strOrNull2(row.transactionId??row.transaction_id),provider:str2(row.provider),providerDisputeId:strOrNull2(row.providerDisputeId??row.provider_dispute_id),ownerType:strOrNull2(row.ownerType??row.owner_type),ownerId:strOrNull2(row.ownerId??row.owner_id),amount:row.amount==null?null:num(row.amount),currency:strOrNull2(row.currency),reason:strOrNull2(row.reason),status:str2(row.status)||"open",resolvedAt:strOrNull2(row.resolvedAt??row.resolved_at)});import{and as and5,eq as eq10,or,sql as sql2}from"drizzle-orm";class MarketplacePayoutService{getDb;provider;logger;t;marketplace;constructor(cfg,marketplace){this.getDb=cfg.getDb,this.provider=cfg.provider,this.logger=cfg.logger.scoped("payment.payout"),this.t=cfg.tables,this.marketplace=marketplace}table(name){return this.t[name]}async requestPayout(input){let db=this.getDb();if(!db)throw Error("Marketplace payouts not ready");let recipientType=input.recipientOwnerType??"seller",currency=input.currency??this.marketplace.config.defaultCurrency;if(input.amount<=0)throw Error("Payout amount must be positive");let minimum=this.marketplace.config.minimumPayoutAmount;if(input.amount<minimum)throw Error(`Payout amount is below the minimum of ${minimum}`);let table=this.table("payoutRequests"),lockKey=`payout:${recipientType}:${input.recipientOwnerId}:${currency}`,insertRow=async(executor,locked)=>{let balance=await this.marketplace.getBalance(recipientType,input.recipientOwnerId,currency,locked);if(input.amount>balance.available)throw Error(`Insufficient available balance: requested ${input.amount}, available ${balance.available}`);let rows=await executor.insert(table).values({recipientOwnerType:recipientType,recipientOwnerId:input.recipientOwnerId,amount:input.amount,currency,status:"pending",destination:input.destination??null,requestedBy:input.requestedBy??null,metadata:JSON.stringify({})}).returning();return rowToPayoutRequest(rows[0])};if(typeof db.transaction==="function")return db.transaction(async(tx)=>{return await tx.execute(sql2`SELECT pg_advisory_xact_lock(hashtext(${lockKey}))`),insertRow(tx,tx)});return insertRow(db)}async listPayoutRequests(filter){let db=this.getDb();if(!db)return[];let table=this.table("payoutRequests"),where=filter.recipientOwnerId?eq10(col4(table,"recipientOwnerId"),filter.recipientOwnerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToPayoutRequest(r));return filter.status?mapped.filter((p)=>p.status===filter.status):mapped}async getPayoutRequest(id){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests"),rows=await db.select().from(table).where(eq10(col4(table,"id"),id)).limit(1);return rows[0]?rowToPayoutRequest(rows[0]):null}async rejectPayout(id,deciderId){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests");return await db.update(table).set({status:"rejected",decidedBy:deciderId??null,decidedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),this.getPayoutRequest(id)}async reconcilePayoutByProvider(providerId,outcome,reason){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests"),row=(await db.select().from(table).where(or(eq10(col4(table,"providerPayoutId"),providerId),eq10(col4(table,"providerTransferId"),providerId))).limit(1))[0];if(!row)return null;let previousStatus=String(row.status),nextStatus=outcome==="completed"?"completed":"failed";if(await db.update(table).set({status:nextStatus,failureReason:outcome==="failed"?reason??"Provider reported failure":null,completedAt:outcome==="completed"?new Date:row.completedAt??null,updatedAt:new Date}).where(eq10(col4(table,"id"),String(row.id))),outcome==="failed"&&previousStatus==="completed")await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:Number(row.amount),currency:String(row.currency),entryType:"adjustment",ownerType:String(row.recipientOwnerType??"seller"),ownerId:String(row.recipientOwnerId??""),referenceType:"payout_request",referenceId:String(row.id),description:`Payout reversal (${reason??"provider failure"})`});return this.getPayoutRequest(String(row.id))}async approvePayout(id,opts){let db=this.getDb();if(!db)return null;let request=await this.getPayoutRequest(id);if(!request)return null;if(request.status!=="pending")throw Error(`Payout is not pending (status: ${request.status})`);if(opts?.deciderId&&request.requestedBy&&opts.deciderId===request.requestedBy)throw Error("Approver cannot be the requester");let table=this.table("payoutRequests"),destination=opts?.destination??request.destination,claimed=await db.update(table).set({status:"processing",decidedBy:opts?.deciderId??null,decidedAt:new Date,updatedAt:new Date}).where(and5(eq10(col4(table,"id"),id),eq10(col4(table,"status"),"pending"))).returning();if(!claimed||claimed.length===0)throw Error("Payout is no longer pending (already being processed)");if(!this.provider)return this.logger.warn("No payment provider configured; payout left in processing",{id}),this.getPayoutRequest(id);let providerId,providerPayoutId;try{let result=destination?await this.provider.createTransfer({amount:request.amount,currency:request.currency,destination,transferGroup:`payout:${id}`,idempotencyKey:`payout:${id}`}):await this.provider.createPayout({amount:request.amount,currency:request.currency,idempotencyKey:`payout:${id}`});if(providerId="providerTransferId"in result?result.providerTransferId:void 0,providerPayoutId="providerPayoutId"in result?result.providerPayoutId:void 0,!result.success)throw Error(result.errorMessage||"Provider payout failed")}catch(err){let reason=err instanceof Error?err.message:"Payout execution failed";return await db.update(table).set({status:"failed",failureReason:reason,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),this.logger.error("Payout provider execution failed",{id,reason}),this.getPayoutRequest(id)}let finalize=async(exec)=>{await exec.update(table).set({status:"completed",provider:this.provider?"configured":null,providerTransferId:providerId??null,providerPayoutId:providerPayoutId??null,completedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"debit",amount:request.amount,currency:request.currency,entryType:"payout",ownerType:request.recipientOwnerType,ownerId:request.recipientOwnerId,referenceType:"payout_request",referenceId:id},exec),await this.marketplace.recordLedgerEntry({account:"payout_completed",direction:"credit",amount:request.amount,currency:request.currency,entryType:"payout",ownerType:request.recipientOwnerType,ownerId:request.recipientOwnerId,referenceType:"payout_request",referenceId:id},exec),await this.consumeSplits(request.recipientOwnerId,request.amount,request.currency,exec)};try{if(typeof db.transaction==="function")await db.transaction(async(tx)=>finalize(tx));else await finalize(db)}catch(err){let reason=err instanceof Error?err.message:"Bookkeeping failed after payout";try{await db.update(table).set({providerTransferId:providerId??null,providerPayoutId:providerPayoutId??null,failureReason:`RECONCILE_REQUIRED: ${reason}`,updatedAt:new Date}).where(eq10(col4(table,"id"),id))}catch{}this.logger.error("Payout bookkeeping failed AFTER funds moved \u2014 reconciliation required",{id,providerId,providerPayoutId,reason})}return this.getPayoutRequest(id)}async consumeSplits(ownerId,amount,currency,executor){let db=executor??this.getDb();if(!db)return;let table=this.table("splits"),splits=await this.marketplace.listSplits({recipientOwnerId:ownerId},executor),remaining=amount,now=Date.now();for(let s of splits){if(remaining<=0)break;if(s.currency!==currency)continue;if(s.status!=="pending"&&s.status!=="available")continue;if(!(s.availableForPayoutAt?new Date(s.availableForPayoutAt).getTime()<=now:!0))continue;if(s.netPayableAmount>remaining)continue;await db.update(table).set({status:"paid",updatedAt:new Date}).where(eq10(col4(table,"id"),s.id)),remaining-=s.netPayableAmount}}async openDispute(input){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),rows=await db.insert(table).values({transactionId:input.transactionId??null,provider:input.provider,providerDisputeId:input.providerDisputeId??null,ownerType:input.ownerType??null,ownerId:input.ownerId??null,amount:input.amount??null,currency:input.currency??null,reason:input.reason??null,status:"open",evidenceDueAt:input.evidenceDueAt??null,metadata:JSON.stringify(input.metadata??{})}).returning(),dispute=rowToDispute(rows[0]);if(input.amount&&input.amount>0&&input.currency)await this.marketplace.recordLedgerEntry({account:"dispute_liability",direction:"credit",amount:input.amount,currency:input.currency,entryType:"dispute",ownerType:input.ownerType,ownerId:input.ownerId,referenceType:"dispute",referenceId:dispute.id});return dispute}async listDisputes(filter){let db=this.getDb();if(!db)return[];let table=this.table("disputes"),where=filter.ownerId?eq10(col4(table,"ownerId"),filter.ownerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToDispute(r));return filter.status?mapped.filter((d)=>d.status===filter.status):mapped}async getDispute(id){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),rows=await db.select().from(table).where(eq10(col4(table,"id"),id)).limit(1);return rows[0]?rowToDispute(rows[0]):null}async resolveDispute(id,outcome){let db=this.getDb();if(!db)return null;let dispute=await this.getDispute(id);if(!dispute)return null;let table=this.table("disputes");if(await db.update(table).set({status:outcome,resolvedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),(outcome==="lost"||outcome==="accepted")&&dispute.amount&&dispute.currency)await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"debit",amount:dispute.amount,currency:dispute.currency,entryType:"dispute",ownerType:dispute.ownerType??void 0,ownerId:dispute.ownerId??void 0,referenceType:"dispute",referenceId:id});return this.getDispute(id)}async reconcileDisputeByProvider(providerDisputeId,status){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),row=(await db.select().from(table).where(eq10(col4(table,"providerDisputeId"),providerDisputeId)).limit(1))[0];if(!row)return null;let dispute=rowToDispute(row);if(dispute.status!=="open")return dispute;let outcome=status==="won"||status==="warning_closed"?"won":status==="lost"?"lost":null;if(!outcome)return dispute;return this.resolveDispute(dispute.id,outcome)}}var col4=(table,name)=>table[name];var init_MarketplacePayoutService=()=>{};import{and as and6,eq as eq11}from"drizzle-orm";class MarketplaceService{getDb;config;logger;t;constructor(cfg){this.getDb=cfg.getDb,this.config=cfg.config,this.logger=cfg.logger.scoped("payment.marketplace"),this.t=cfg.tables}table(name){return this.t[name]}async recordLedgerEntry(entry,executor){let db=executor??this.getDb();if(!db)return;await db.insert(this.table("ledgerEntries")).values({account:entry.account,ownerType:entry.ownerType??null,ownerId:entry.ownerId??null,direction:entry.direction,amount:entry.amount,currency:entry.currency,entryType:entry.entryType,referenceType:entry.referenceType??null,referenceId:entry.referenceId??null,splitId:entry.splitId??null,description:entry.description??null,metadata:JSON.stringify({})})}async getSettlementPolicy(ownerType,ownerId){let db=this.getDb();if(!db)return null;let table=this.table("settlementPolicies"),rows=await db.select().from(table).where(and6(eq11(col5(table,"ownerType"),ownerType),eq11(col5(table,"ownerId"),ownerId))).limit(1);return rows[0]?rowToSettlementPolicy(rows[0]):null}async upsertSettlementPolicy(input){let db=this.getDb();if(!db)return null;let table=this.table("settlementPolicies"),existing=await this.getSettlementPolicy(input.ownerType,input.ownerId),values={ownerType:input.ownerType,ownerId:input.ownerId,defaultDelayDays:input.defaultDelayDays??this.config.defaultSettlementDelayDays,newSellerDelayDays:input.newSellerDelayDays??this.config.newSellerDelayDays,reserveRate:String(input.reserveRate??this.config.reserveRate),reserveDays:input.reserveDays??this.config.reserveReleaseDays,minimumPayoutAmount:input.minimumPayoutAmount??this.config.minimumPayoutAmount,currency:input.currency??null,earlyPayoutEnabled:input.earlyPayoutEnabled??!1,updatedAt:new Date};if(existing)await db.update(table).set(values).where(eq11(col5(table,"id"),existing.id));else await db.insert(table).values(values);return this.getSettlementPolicy(input.ownerType,input.ownerId)}async recordSplit(input){let db=this.getDb();if(!db)return null;let recipientType=input.recipientOwnerType??"seller",currency=input.currency??this.config.defaultCurrency,now=new Date,policy=await this.getSettlementPolicy(recipientType,input.recipientOwnerId),breakdown=computeSplitBreakdown(input,this.config,policy,now),splitTable=this.table("splits"),rows=await db.insert(splitTable).values({transactionId:input.transactionId??null,recipientOwnerType:recipientType,recipientOwnerId:input.recipientOwnerId,provider:input.provider,grossAmount:breakdown.grossAmount,providerFeeAmount:breakdown.providerFeeAmount,platformFeeAmount:breakdown.platformFeeAmount,reserveAmount:breakdown.reserveAmount,netPayableAmount:breakdown.netPayableAmount,currency,availableForPayoutAt:breakdown.availableForPayoutAt,status:"pending",sourceType:input.sourceType??null,sourceId:input.sourceId??null,metadata:JSON.stringify(input.metadata??{})}).returning(),split=rowToSplit(rows[0]);if(await this.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:breakdown.netPayableAmount,currency,entryType:"split",ownerType:recipientType,ownerId:input.recipientOwnerId,referenceType:"split",referenceId:split.id,splitId:split.id}),breakdown.platformFeeAmount>0)await this.recordLedgerEntry({account:"platform_revenue",direction:"credit",amount:breakdown.platformFeeAmount,currency,entryType:"split",referenceType:"split",referenceId:split.id,splitId:split.id});if(breakdown.providerFeeAmount>0)await this.recordLedgerEntry({account:"provider_fee",direction:"credit",amount:breakdown.providerFeeAmount,currency,entryType:"split",referenceType:"split",referenceId:split.id,splitId:split.id});if(breakdown.reserveAmount>0){let reserveDays=policy?.reserveDays??this.config.reserveReleaseDays;await db.insert(this.table("reserves")).values({ownerType:recipientType,ownerId:input.recipientOwnerId,splitId:split.id,amount:breakdown.reserveAmount,currency,reason:input.isNewSeller?"new_seller":"refund_window",status:"held",releaseAt:reserveDays>0?addDays(now,reserveDays):null,metadata:JSON.stringify({})}),await this.recordLedgerEntry({account:"reserve",direction:"credit",amount:breakdown.reserveAmount,currency,entryType:"reserve_hold",ownerType:recipientType,ownerId:input.recipientOwnerId,referenceType:"split",referenceId:split.id,splitId:split.id})}return this.logger.info("Recorded split",{splitId:split.id,gross:breakdown.grossAmount,netPayable:breakdown.netPayableAmount,reserve:breakdown.reserveAmount}),split}async listSplits(filter,executor){let db=executor??this.getDb();if(!db)return[];let table=this.table("splits"),where=filter.recipientOwnerId?eq11(col5(table,"recipientOwnerId"),filter.recipientOwnerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToSplit(r));return filter.status?mapped.filter((s)=>s.status===filter.status):mapped}async releaseReserve(id){let db=this.getDb();if(!db)return!1;let table=this.table("reserves"),row=(await db.select().from(table).where(eq11(col5(table,"id"),id)).limit(1))[0];if(!row||String(row.status)!=="held")return!1;await db.update(table).set({status:"released",releasedAt:new Date,updatedAt:new Date}).where(eq11(col5(table,"id"),id));let reserveOwnerType=String(row.ownerType??row.owner_type??""),reserveOwnerId=String(row.ownerId??row.owner_id??"");return await this.recordLedgerEntry({account:"reserve",direction:"debit",amount:Number(row.amount),currency:String(row.currency),entryType:"reserve_release",ownerType:reserveOwnerType,ownerId:reserveOwnerId,referenceType:"reserve",referenceId:id}),await this.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:Number(row.amount),currency:String(row.currency),entryType:"reserve_release",ownerType:reserveOwnerType,ownerId:reserveOwnerId,referenceType:"reserve",referenceId:id}),!0}async autoReleaseDueReserves(ownerType,ownerId){if(!this.config.autoReleaseReserves)return;let db=this.getDb();if(!db)return;let table=this.table("reserves"),rows=await db.select().from(table).where(and6(eq11(col5(table,"ownerType"),ownerType),eq11(col5(table,"ownerId"),ownerId))),now=Date.now();for(let r of rows){let releaseAt=r.releaseAt??r.release_at;if(String(r.status)==="held"&&releaseAt&&new Date(String(releaseAt)).getTime()<=now)await this.releaseReserve(String(r.id))}}async getBalance(ownerType,ownerId,currency,executor){let cur=currency??this.config.defaultCurrency,db=executor??this.getDb(),base={ownerType,ownerId,currency:cur,available:0,pending:0,reserved:0,payoutInFlight:0};if(!db)return base;if(!executor)await this.autoReleaseDueReserves(ownerType,ownerId);let splits=await this.listSplits({recipientOwnerId:ownerId},executor),now=Date.now();for(let s of splits){if(s.currency!==cur)continue;if(s.status!=="pending"&&s.status!=="available")continue;if(!(s.availableForPayoutAt?new Date(s.availableForPayoutAt).getTime()<=now:!0))base.pending+=s.netPayableAmount}let ledgerTable=this.table("ledgerEntries"),ledgerRows=await db.select().from(ledgerTable).where(and6(eq11(col5(ledgerTable,"ownerType"),ownerType),eq11(col5(ledgerTable,"ownerId"),ownerId))),payableNet=0;for(let e of ledgerRows){if(String(e.account)!=="seller_payable"||String(e.currency)!==cur)continue;let amt=Number(e.amount);payableNet+=String(e.direction)==="credit"?amt:-amt}base.available=payableNet-base.pending;let reserveTable=this.table("reserves"),reserveRows=await db.select().from(reserveTable).where(and6(eq11(col5(reserveTable,"ownerType"),ownerType),eq11(col5(reserveTable,"ownerId"),ownerId)));for(let r of reserveRows)if(String(r.status)==="held"&&String(r.currency)===cur)base.reserved+=Number(r.amount);let payoutTable=this.table("payoutRequests"),payoutRows=await db.select().from(payoutTable).where(and6(eq11(col5(payoutTable,"recipientOwnerType"),ownerType),eq11(col5(payoutTable,"recipientOwnerId"),ownerId)));for(let p of payoutRows){let st=String(p.status);if((st==="pending"||st==="approved"||st==="processing")&&String(p.currency)===cur)base.payoutInFlight+=Number(p.amount),base.available-=Number(p.amount)}if(base.available<0)base.available=0;return base}}var col5=(table,name)=>table[name];var init_MarketplaceService=()=>{};var pick=(schemaTables,camel,snake)=>schemaTables[camel]??schemaTables[snake]??{},createMarketplaceServices=(params)=>{let config=resolveMarketplaceConfig(params.options.payment);if(!config.enabled)return null;let tables={splits:pick(params.schemaTables,"paymentSplits","payment_splits"),ledgerEntries:pick(params.schemaTables,"paymentLedgerEntries","payment_ledger_entries"),settlementPolicies:pick(params.schemaTables,"paymentSettlementPolicies","payment_settlement_policies"),reserves:pick(params.schemaTables,"paymentReserves","payment_reserves"),payoutRequests:pick(params.schemaTables,"paymentPayoutRequests","payment_payout_requests"),disputes:pick(params.schemaTables,"paymentDisputes","payment_disputes")},serviceConfig={getDb:params.getDb,provider:params.provider,config,logger:params.logger,tables},marketplaceService=new MarketplaceService(serviceConfig),payoutService=new MarketplacePayoutService(serviceConfig,marketplaceService);return{marketplaceService,payoutService}};var init_Marketplace=__esm(()=>{init_MarketplacePayoutService();init_MarketplaceService();init_MarketplacePayoutService();init_MarketplaceService()});var loadStripe=(config)=>{try{let StripeModule=__require("stripe");return new(StripeModule.default??StripeModule)(config.secretKey,{maxNetworkRetries:config.maxNetworkRetries??2,timeout:config.timeout??30000,...config.apiVersion?{apiVersion:config.apiVersion}:{}})}catch(error){return logger.scoped("payment.init").error("Failed to load stripe package. Install it: bun add stripe",error),null}},createStripeProvider=(config)=>{let stripe=loadStripe(config);if(!stripe)return null;let providerName="stripe";return{get name(){return providerName},async initialize3DS(request){try{let params={amount:Math.round(Number(request.paidPrice)*100),currency:request.currency.toLowerCase(),payment_method_types:["card"],description:`Order ${request.conversationId}`,metadata:{conversationId:request.conversationId,basketId:request.basketId??"",buyerEmail:request.buyer.email,buyerId:request.buyer.id}};if("cardToken"in request.paymentCard&&request.paymentCard.cardToken)params.payment_method=request.paymentCard.cardToken,params.confirm=!0,params.return_url=request.callbackUrl;let paymentIntent=await stripe.paymentIntents.create(params),clientSecret=paymentIntent.client_secret,piId=paymentIntent.id,status=paymentIntent.status;return{success:!0,paymentId:piId,threeDSHtmlContent:clientSecret,signature:status,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment initialization failed",rawResponse:{error:err.message}}}},async complete3DS(request){try{let paymentIntent=await stripe.paymentIntents.retrieve(request.paymentId,{expand:["payment_method"]}),status=paymentIntent.status,isSuccess=status==="succeeded",amountReceived=paymentIntent.amount_received??0,card=paymentIntent.payment_method?.card;return{success:isSuccess,paymentId:paymentIntent.id,price:amountReceived/100,paidPrice:amountReceived/100,currency:paymentIntent.currency?.toUpperCase(),cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,cardFamily:card?.funding??void 0,lastFourDigits:card?.last4??void 0,authCode:status,errorCode:isSuccess?void 0:"PAYMENT_NOT_SUCCEEDED",errorMessage:isSuccess?void 0:`Payment status: ${status}`,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment completion failed",rawResponse:{error:err.message}}}},async getPaymentDetail(request){try{let paymentIntent=await stripe.paymentIntents.retrieve(request.paymentId,{expand:["payment_method"]}),amountReceived=paymentIntent.amount_received??paymentIntent.amount??0,card=paymentIntent.payment_method?.card;return{success:!0,paymentId:paymentIntent.id,price:amountReceived/100,paidPrice:amountReceived/100,currency:paymentIntent.currency?.toUpperCase(),cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,lastFourDigits:card?.last4??void 0,binNumber:void 0,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment detail query failed",rawResponse:{error:err.message}}}},async queryBin(_request){return{success:!1,errorCode:"NOT_SUPPORTED",errorMessage:"BIN query is not supported by Stripe. Card info is available after payment method creation.",rawResponse:{}}},async saveCard(request){try{let customer=await stripe.customers.create({email:request.email,metadata:{externalId:request.externalId??""}}),paymentMethod=await stripe.paymentMethods.create({type:"card",card:{number:request.card.cardNumber,exp_month:Number(request.card.expireMonth),exp_year:Number(request.card.expireYear)}});await stripe.paymentMethods.attach(paymentMethod.id,{customer:customer.id});let card=paymentMethod.card;return{success:!0,cardUserKey:customer.id,cardToken:paymentMethod.id,cardAlias:request.card.cardAlias,binNumber:void 0,lastFourDigits:card?.last4??void 0,cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,cardFamily:card?.funding??void 0,cardBankName:void 0,rawResponse:{customer,paymentMethod}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to save card",rawResponse:{error:err.message}}}},async listCards(request){try{let response=await stripe.paymentMethods.list({customer:request.cardUserKey,type:"card"}),cards=response.data.map((pm)=>{let card=pm.card;return{cardToken:pm.id,cardAlias:pm.metadata?.alias??"",binNumber:"",lastFourDigits:card?.last4??"",cardType:card?.brand??"",cardAssociation:card?.brand??"",cardFamily:card?.funding??"",cardBankName:"",cardBankCode:0}});return{success:!0,cardUserKey:request.cardUserKey,cards,rawResponse:response}}catch(error){let err=error;return{success:!1,cards:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list cards",rawResponse:{error:err.message}}}},async deleteCard(request){try{return await stripe.paymentMethods.detach(request.cardToken),{success:!0,rawResponse:{detached:request.cardToken}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to delete card",rawResponse:{error:err.message}}}},async refund(request){try{let amountInCents=Math.round(Number(request.price)*100),refund=await stripe.refunds.create({payment_intent:request.paymentTransactionId,amount:amountInCents});return{success:refund.status==="succeeded",paymentId:refund.payment_intent,price:amountInCents/100,currency:refund.currency?.toUpperCase(),errorCode:refund.status!=="succeeded"?"REFUND_FAILED":void 0,errorMessage:refund.status!=="succeeded"?`Refund status: ${refund.status}`:void 0,rawResponse:refund}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Refund failed",rawResponse:{error:err.message}}}},parseCallback(formData){let paymentIntent=formData.payment_intent??formData.paymentId??"",redirectStatus=formData.redirect_status??"";return{success:redirectStatus==="succeeded",paymentId:paymentIntent,conversationId:formData.conversationId??"",mdStatus:redirectStatus,rawData:formData}},async registerSubMerchant(request){try{let account=await stripe.accounts.create({type:"express",country:request.currency==="TRY"?"TR":void 0,email:request.email,business_type:request.subMerchantType==="personal"?"individual":"company",metadata:{externalId:request.subMerchantExternalId,name:request.name,contactName:request.contactName??"",contactSurname:request.contactSurname??""},capabilities:{transfers:{requested:!0}}});return{success:!0,subMerchantKey:account.id,rawResponse:account}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create connected account",rawResponse:{error:err.message}}}},async updateSubMerchant(request){try{let params={metadata:{}},meta=params.metadata;if(request.name)meta.name=request.name;if(request.email)params.email=request.email;if(request.contactName)meta.contactName=request.contactName;if(request.contactSurname)meta.contactSurname=request.contactSurname;return await stripe.accounts.update(request.subMerchantKey,params),{success:!0,rawResponse:{updated:request.subMerchantKey}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update connected account",rawResponse:{error:err.message}}}},async getSubMerchant(request){try{let account=await stripe.accounts.retrieve(request.subMerchantExternalId),meta=account.metadata??{};return{success:!0,subMerchantKey:account.id,name:meta.name,email:account.email,subMerchantType:account.business_type==="individual"?"personal":"company",currency:account.default_currency?.toUpperCase(),rawResponse:account}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to retrieve connected account",rawResponse:{error:err.message}}}},async approveSplit(request){return{success:!0,paymentTransactionId:request.paymentTransactionId,rawResponse:{message:"Stripe transfers are automatic; no manual approval needed."}}},async disapproveSplit(request){try{let reversal=await stripe.transfers.createReversal(request.paymentTransactionId);return{success:!0,paymentTransactionId:reversal.id,rawResponse:reversal}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to reverse transfer",rawResponse:{error:err.message}}}},async createProduct(request){try{let params={name:request.name};if(request.description)params.description=request.description;if(request.type)params.type=request.type;if(request.images)params.images=request.images;if(request.unitLabel)params.unit_label=request.unitLabel;if(request.url)params.url=request.url;if(request.metadata)params.metadata=request.metadata;let product=await stripe.products.create(params);return{success:!0,providerProductId:product.id,name:product.name,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create product",rawResponse:{error:err.message}}}},async updateProduct(request){try{let params={};if(request.name)params.name=request.name;if(request.description!==void 0)params.description=request.description;if(request.images)params.images=request.images;if(request.unitLabel!==void 0)params.unit_label=request.unitLabel;if(request.url!==void 0)params.url=request.url;if(request.active!==void 0)params.active=request.active;if(request.metadata)params.metadata=request.metadata;let product=await stripe.products.update(request.providerProductId,params);return{success:!0,providerProductId:product.id,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update product",rawResponse:{error:err.message}}}},async getProduct(request){try{let product=await stripe.products.retrieve(request.providerProductId);return{success:!0,providerProductId:product.id,name:product.name,description:product.description,type:product.type,active:product.active,images:product.images,unitLabel:product.unit_label,url:product.url,metadata:product.metadata,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get product",rawResponse:{error:err.message}}}},async listProducts(request){try{let params={};if(request.active!==void 0)params.active=request.active;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.products.list(params);return{success:!0,products:response.data.map((p)=>({providerProductId:p.id,name:p.name,description:p.description,type:p.type,active:p.active,images:p.images,unitLabel:p.unit_label,metadata:p.metadata})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,products:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list products",rawResponse:{error:err.message}}}},async archiveProduct(request){try{return await stripe.products.update(request.providerProductId,{active:!1}),{success:!0,rawResponse:{archived:request.providerProductId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to archive product",rawResponse:{error:err.message}}}},async createPrice(request){try{let params={product:request.providerProductId,currency:request.currency.toLowerCase(),unit_amount:request.unitAmount};if(request.nickname)params.nickname=request.nickname;if(request.billingScheme)params.billing_scheme=request.billingScheme;if(request.unitAmountDecimal)params.unit_amount_decimal=request.unitAmountDecimal;if(request.metadata)params.metadata=request.metadata;if(request.recurring)params.recurring={interval:request.recurring.interval,...request.recurring.intervalCount?{interval_count:request.recurring.intervalCount}:{},...request.recurring.usageType?{usage_type:request.recurring.usageType}:{},...request.recurring.aggregateUsage?{aggregate_usage:request.recurring.aggregateUsage}:{}};if(request.transformQuantity)params.transform_quantity={divide_by:request.transformQuantity.divideBy,round:request.transformQuantity.round};let price=await stripe.prices.create(params);return{success:!0,providerPriceId:price.id,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create price",rawResponse:{error:err.message}}}},async updatePrice(request){try{let params={};if(request.nickname!==void 0)params.nickname=request.nickname;if(request.active!==void 0)params.active=request.active;if(request.metadata)params.metadata=request.metadata;let price=await stripe.prices.update(request.providerPriceId,params);return{success:!0,providerPriceId:price.id,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update price",rawResponse:{error:err.message}}}},async getPrice(request){try{let price=await stripe.prices.retrieve(request.providerPriceId),rec=price.recurring;return{success:!0,providerPriceId:price.id,providerProductId:price.product,currency:price.currency?.toUpperCase(),unitAmount:price.unit_amount,type:price.type,billingScheme:price.billing_scheme,nickname:price.nickname,active:price.active,recurring:rec?{interval:rec.interval,intervalCount:rec.interval_count??1,usageType:rec.usage_type}:void 0,metadata:price.metadata,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get price",rawResponse:{error:err.message}}}},async listPrices(request){try{let params={};if(request.providerProductId)params.product=request.providerProductId;if(request.active!==void 0)params.active=request.active;if(request.type)params.type=request.type;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.prices.list(params);return{success:!0,prices:response.data.map((p)=>{let rec=p.recurring;return{providerPriceId:p.id,providerProductId:p.product,currency:p.currency?.toUpperCase(),unitAmount:p.unit_amount,type:p.type,billingScheme:p.billing_scheme,nickname:p.nickname,active:p.active,recurring:rec?{interval:rec.interval,intervalCount:rec.interval_count??1,usageType:rec.usage_type}:void 0,metadata:p.metadata}}),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,prices:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list prices",rawResponse:{error:err.message}}}},async archivePrice(request){try{return await stripe.prices.update(request.providerPriceId,{active:!1}),{success:!0,rawResponse:{archived:request.providerPriceId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to archive price",rawResponse:{error:err.message}}}},async createCustomer(request){try{let params={email:request.email};if(request.name)params.name=request.name;if(request.phone)params.phone=request.phone;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.address)params.address={line1:request.address.line1,line2:request.address.line2,city:request.address.city,state:request.address.state,postal_code:request.address.postalCode,country:request.address.country};let customer=await stripe.customers.create(params);if(request.taxId)try{let createTaxId=stripe.taxIds?.create;if(createTaxId)await createTaxId({customer:customer.id,type:request.taxId.type,value:request.taxId.value})}catch(_){}return{success:!0,providerCustomerId:customer.id,email:customer.email,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create customer",rawResponse:{error:err.message}}}},async updateCustomer(request){try{let params={};if(request.email)params.email=request.email;if(request.name!==void 0)params.name=request.name;if(request.phone!==void 0)params.phone=request.phone;if(request.description!==void 0)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.address)params.address={line1:request.address.line1,line2:request.address.line2,city:request.address.city,state:request.address.state,postal_code:request.address.postalCode,country:request.address.country};let customer=await stripe.customers.update(request.providerCustomerId,params);return{success:!0,providerCustomerId:customer.id,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update customer",rawResponse:{error:err.message}}}},async getCustomer(request){try{let customer=await stripe.customers.retrieve(request.providerCustomerId);return{success:!0,providerCustomerId:customer.id,email:customer.email,name:customer.name,phone:customer.phone,description:customer.description,metadata:customer.metadata,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get customer",rawResponse:{error:err.message}}}},async listCustomers(request){try{let params={};if(request.email)params.email=request.email;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.customers.list(params);return{success:!0,customers:response.data.map((c)=>({providerCustomerId:c.id,email:c.email,name:c.name,phone:c.phone})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,customers:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list customers",rawResponse:{error:err.message}}}},async deleteCustomer(request){try{return await stripe.customers.del(request.providerCustomerId),{success:!0,rawResponse:{deleted:request.providerCustomerId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to delete customer",rawResponse:{error:err.message}}}},async createSubscription(request){try{let params={customer:request.providerCustomerId,items:request.items.map((i)=>({price:i.providerPriceId,...i.quantity!==void 0?{quantity:i.quantity}:{}}))};if(request.trialPeriodDays)params.trial_period_days=request.trialPeriodDays;if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.cancelAtPeriodEnd!==void 0)params.cancel_at_period_end=request.cancelAtPeriodEnd;if(request.metadata)params.metadata=request.metadata;if(request.defaultPaymentMethod)params.default_payment_method=request.defaultPaymentMethod;params.payment_behavior="default_incomplete",params.payment_settings={save_default_payment_method:"on_subscription"},params.expand=["latest_invoice.payment_intent"];let sub=await stripe.subscriptions.create(params),pi=sub.latest_invoice?.payment_intent;return{success:!0,providerSubscriptionId:sub.id,status:sub.status,currentPeriodStart:sub.current_period_start?new Date(sub.current_period_start*1000).toISOString():void 0,currentPeriodEnd:sub.current_period_end?new Date(sub.current_period_end*1000).toISOString():void 0,clientSecret:pi?.client_secret,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create subscription",rawResponse:{error:err.message}}}},async updateSubscription(request){try{let params={};if(request.items)params.items=request.items.map((i)=>({...i.id?{id:i.id}:{},price:i.providerPriceId,...i.quantity!==void 0?{quantity:i.quantity}:{}}));if(request.cancelAtPeriodEnd!==void 0)params.cancel_at_period_end=request.cancelAtPeriodEnd;if(request.trialEnd!==void 0)params.trial_end=request.trialEnd;if(request.metadata)params.metadata=request.metadata;if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.defaultPaymentMethod)params.default_payment_method=request.defaultPaymentMethod;if(request.prorationBehavior)params.proration_behavior=request.prorationBehavior;let sub=await stripe.subscriptions.update(request.providerSubscriptionId,params);return{success:!0,providerSubscriptionId:sub.id,status:sub.status,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update subscription",rawResponse:{error:err.message}}}},async cancelSubscription(request){try{if(request.cancelAtPeriodEnd){let sub2=await stripe.subscriptions.update(request.providerSubscriptionId,{cancel_at_period_end:!0});return{success:!0,providerSubscriptionId:sub2.id,status:sub2.status,rawResponse:sub2}}let params={};if(request.invoiceNow)params.invoice_now=request.invoiceNow;if(request.prorate)params.prorate=request.prorate;let sub=await stripe.subscriptions.cancel(request.providerSubscriptionId,params);return{success:!0,providerSubscriptionId:sub.id,status:sub.status,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to cancel subscription",rawResponse:{error:err.message}}}},async getSubscription(request){try{let sub=await stripe.subscriptions.retrieve(request.providerSubscriptionId),itemsData=sub.items?.data??[];return{success:!0,providerSubscriptionId:sub.id,providerCustomerId:sub.customer,status:sub.status,currentPeriodStart:sub.current_period_start?new Date(sub.current_period_start*1000).toISOString():void 0,currentPeriodEnd:sub.current_period_end?new Date(sub.current_period_end*1000).toISOString():void 0,cancelAtPeriodEnd:sub.cancel_at_period_end,canceledAt:sub.canceled_at?new Date(sub.canceled_at*1000).toISOString():void 0,trialStart:sub.trial_start?new Date(sub.trial_start*1000).toISOString():void 0,trialEnd:sub.trial_end?new Date(sub.trial_end*1000).toISOString():void 0,items:itemsData.map((i)=>({id:i.id,providerPriceId:i.price?.id??i.price,quantity:i.quantity??1})),metadata:sub.metadata,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get subscription",rawResponse:{error:err.message}}}},async listSubscriptions(request){try{let params={};if(request.providerCustomerId)params.customer=request.providerCustomerId;if(request.status)params.status=request.status;if(request.providerPriceId)params.price=request.providerPriceId;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.subscriptions.list(params);return{success:!0,subscriptions:response.data.map((s)=>({providerSubscriptionId:s.id,providerCustomerId:s.customer,status:s.status,currentPeriodStart:s.current_period_start?new Date(s.current_period_start*1000).toISOString():void 0,currentPeriodEnd:s.current_period_end?new Date(s.current_period_end*1000).toISOString():void 0,cancelAtPeriodEnd:s.cancel_at_period_end})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,subscriptions:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list subscriptions",rawResponse:{error:err.message}}}},async reportUsage(request){try{let params={quantity:request.quantity,action:request.action??"increment"};if(request.timestamp)params.timestamp=request.timestamp;let record=await stripe.subscriptionItems.usageRecords.create(request.subscriptionItemId,params);return{success:!0,usageRecordId:record.id,quantity:record.quantity,timestamp:record.timestamp,rawResponse:record}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to report usage",rawResponse:{error:err.message}}}},async listUsageRecordSummaries(request){try{let params={};if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.subscriptionItems.usageRecordSummaries.list(request.subscriptionItemId,params);return{success:!0,summaries:response.data.map((s)=>({id:s.id,totalUsage:s.total_usage,period:s.period,subscriptionItem:s.subscription_item})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,summaries:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list usage summaries",rawResponse:{error:err.message}}}},async createInvoice(request){try{let params={customer:request.providerCustomerId};if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.autoAdvance!==void 0)params.auto_advance=request.autoAdvance;let invoice=await stripe.invoices.create(params);if(request.items?.length)for(let item of request.items){let lineParams={customer:request.providerCustomerId,invoice:invoice.id};if(item.providerPriceId)lineParams.price=item.providerPriceId;if(item.quantity)lineParams.quantity=item.quantity;if(item.unitAmount!==void 0)lineParams.unit_amount=item.unitAmount;if(item.currency)lineParams.currency=item.currency;if(item.description)lineParams.description=item.description;await stripe.invoiceItems.create(lineParams)}return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create invoice",rawResponse:{error:err.message}}}},async finalizeInvoice(request){try{let params={};if(request.autoAdvance!==void 0)params.auto_advance=request.autoAdvance;let invoice=await stripe.invoices.finalizeInvoice(request.providerInvoiceId,params);return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to finalize invoice",rawResponse:{error:err.message}}}},async sendInvoice(request){try{let invoice=await stripe.invoices.sendInvoice(request.providerInvoiceId);return{success:!0,providerInvoiceId:invoice.id,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to send invoice",rawResponse:{error:err.message}}}},async voidInvoice(request){try{let invoice=await stripe.invoices.voidInvoice(request.providerInvoiceId);return{success:!0,providerInvoiceId:invoice.id,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to void invoice",rawResponse:{error:err.message}}}},async payInvoice(request){try{let params={};if(request.paymentMethod)params.payment_method=request.paymentMethod;let invoice=await stripe.invoices.pay(request.providerInvoiceId,params);return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,amountPaid:invoice.amount_paid,amountDue:invoice.amount_due,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to pay invoice",rawResponse:{error:err.message}}}},async getInvoice(request){try{let invoice=await stripe.invoices.retrieve(request.providerInvoiceId),linesData=invoice.lines?.data??[];return{success:!0,providerInvoiceId:invoice.id,providerCustomerId:invoice.customer,providerSubscriptionId:invoice.subscription,status:invoice.status,amountDue:invoice.amount_due,amountPaid:invoice.amount_paid,amountRemaining:invoice.amount_remaining,currency:invoice.currency?.toUpperCase(),description:invoice.description,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,dueDate:invoice.due_date?new Date(invoice.due_date*1000).toISOString():void 0,periodStart:invoice.period_start?new Date(invoice.period_start*1000).toISOString():void 0,periodEnd:invoice.period_end?new Date(invoice.period_end*1000).toISOString():void 0,metadata:invoice.metadata,lines:linesData.map((l)=>({id:l.id,description:l.description,amount:l.amount,currency:l.currency?.toUpperCase(),quantity:l.quantity,providerPriceId:l.price?.id})),rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get invoice",rawResponse:{error:err.message}}}},async listInvoices(request){try{let params={};if(request.providerCustomerId)params.customer=request.providerCustomerId;if(request.providerSubscriptionId)params.subscription=request.providerSubscriptionId;if(request.status)params.status=request.status;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.invoices.list(params);return{success:!0,invoices:response.data.map((inv)=>({providerInvoiceId:inv.id,providerCustomerId:inv.customer,status:inv.status,amountDue:inv.amount_due,amountPaid:inv.amount_paid,currency:inv.currency?.toUpperCase(),dueDate:inv.due_date?new Date(inv.due_date*1000).toISOString():void 0,hostedInvoiceUrl:inv.hosted_invoice_url})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,invoices:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list invoices",rawResponse:{error:err.message}}}},async getBalance(){try{let bal=await stripe.balance.retrieve(),mapAmounts=(arr)=>(arr??[]).map((a)=>({amount:a.amount,currency:a.currency?.toUpperCase()}));return{success:!0,available:mapAmounts(bal.available),pending:mapAmounts(bal.pending),connectReserved:mapAmounts(bal.connect_reserved),rawResponse:bal}}catch(error){let err=error;return{success:!1,available:[],pending:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get balance",rawResponse:{error:err.message}}}},async createPayout(request){try{let params={amount:request.amount,currency:request.currency.toLowerCase()};if(request.destination)params.destination=request.destination;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.method)params.method=request.method;if(request.sourceType)params.source_type=request.sourceType;if(request.statementDescriptor)params.statement_descriptor=request.statementDescriptor;let payout=await stripe.payouts.create(params,request.idempotencyKey?{idempotencyKey:request.idempotencyKey}:void 0);return{success:!0,providerPayoutId:payout.id,amount:payout.amount,currency:payout.currency?.toUpperCase(),status:payout.status,arrivalDate:payout.arrival_date?new Date(payout.arrival_date*1000).toISOString():void 0,method:payout.method,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create payout",rawResponse:{error:err.message}}}},async getPayout(request){try{let payout=await stripe.payouts.retrieve(request.providerPayoutId);return{success:!0,providerPayoutId:payout.id,amount:payout.amount,currency:payout.currency?.toUpperCase(),status:payout.status,arrivalDate:payout.arrival_date?new Date(payout.arrival_date*1000).toISOString():void 0,method:payout.method,description:payout.description,destination:payout.destination,metadata:payout.metadata,failureCode:payout.failure_code,failureMessage:payout.failure_message,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get payout",rawResponse:{error:err.message}}}},async cancelPayout(request){try{let payout=await stripe.payouts.cancel(request.providerPayoutId);return{success:!0,providerPayoutId:payout.id,status:payout.status,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to cancel payout",rawResponse:{error:err.message}}}},async listPayouts(request){try{let params={};if(request.status)params.status=request.status;if(request.destination)params.destination=request.destination;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;if(request.arrivalDateGte||request.arrivalDateLte){let arrival={};if(request.arrivalDateGte)arrival.gte=request.arrivalDateGte;if(request.arrivalDateLte)arrival.lte=request.arrivalDateLte;params.arrival_date=arrival}let response=await stripe.payouts.list(params);return{success:!0,payouts:response.data.map((p)=>({providerPayoutId:p.id,amount:p.amount,currency:p.currency?.toUpperCase(),status:p.status,arrivalDate:p.arrival_date?new Date(p.arrival_date*1000).toISOString():void 0,method:p.method,description:p.description})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,payouts:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list payouts",rawResponse:{error:err.message}}}},async createTransfer(request){try{let params={amount:request.amount,currency:request.currency.toLowerCase(),destination:request.destination};if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.sourceTransaction)params.source_transaction=request.sourceTransaction;if(request.transferGroup)params.transfer_group=request.transferGroup;let transfer=await stripe.transfers.create(params,request.idempotencyKey?{idempotencyKey:request.idempotencyKey}:void 0);return{success:!0,providerTransferId:transfer.id,amount:transfer.amount,currency:transfer.currency?.toUpperCase(),destination:transfer.destination,rawResponse:transfer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create transfer",rawResponse:{error:err.message}}}},async listTransfers(request){try{let params={};if(request.destination)params.destination=request.destination;if(request.transferGroup)params.transfer_group=request.transferGroup;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.transfers.list(params);return{success:!0,transfers:response.data.map((t)=>({providerTransferId:t.id,amount:t.amount,currency:t.currency?.toUpperCase(),destination:t.destination,description:t.description,created:t.created?new Date(t.created*1000).toISOString():void 0})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,transfers:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list transfers",rawResponse:{error:err.message}}}}}},verifyStripeWebhook=async(rawBody,signature,webhookSecret)=>{try{let StripeModule=__require("stripe"),Stripe=StripeModule.default??StripeModule,stripe=new Stripe("sk_dummy_for_webhook_verification"),factory=Stripe,cryptoProvider=typeof factory.createSubtleCryptoProvider==="function"?factory.createSubtleCryptoProvider():typeof factory.createNodeCryptoProvider==="function"?factory.createNodeCryptoProvider():void 0;return{success:!0,event:await stripe.webhooks.constructEventAsync(rawBody,signature,webhookSecret,void 0,cryptoProvider)}}catch(error){return{success:!1,event:null,error:error instanceof Error?error.message:"Webhook verification failed"}}};var init_StripeProvider=__esm(()=>{init_Logger2()});var resolveEnvValue3=(value)=>{let envValue=process.env[value];if(envValue)return envValue;return value},createPaymentService=(config)=>{let paymentConfig=config.payment;if(!paymentConfig?.enabled)return null;let log2=logger.scoped("payment.init"),providerName=paymentConfig.provider;if(!providerName)return log2.warn("payment.enabled=true but no provider specified"),null;let providerConfig=null,provider=null,webhookSecret;switch(providerName){case"iyzico":{let iyzicoConf=paymentConfig.iyzico;if(!iyzicoConf)return log2.warn("payment.provider=iyzico but no iyzico config block"),null;let apiUrl=resolveEnvValue3(iyzicoConf.apiUrl),apiKey=resolveEnvValue3(iyzicoConf.apiKey),secretKey=resolveEnvValue3(iyzicoConf.secretKey);if(!apiUrl||!apiKey||!secretKey)return log2.warn("iyzico credentials missing \u2014 ensure env vars are set",{apiUrl:iyzicoConf.apiUrl,apiKeyEnv:iyzicoConf.apiKey,secretKeyEnv:iyzicoConf.secretKey}),null;providerConfig={name:"iyzico",apiUrl,apiKey,secretKey},provider=createIyzicoProvider(providerConfig);break}case"stripe":{let stripeConf=paymentConfig.stripe;if(!stripeConf)return log2.warn("payment.provider=stripe but no stripe config block"),null;let secretKey=resolveEnvValue3(stripeConf.secretKey),whSecret=resolveEnvValue3(stripeConf.webhookSecret);if(!secretKey)return log2.warn("Stripe secret key missing \u2014 ensure env var is set",{secretKeyEnv:stripeConf.secretKey}),null;if(!whSecret)log2.warn("Stripe webhook secret missing \u2014 webhook verification will fail",{webhookSecretEnv:stripeConf.webhookSecret});webhookSecret=whSecret,provider=createStripeProvider({secretKey,webhookSecret:whSecret,apiVersion:stripeConf.apiVersion,maxNetworkRetries:stripeConf.maxNetworkRetries,timeout:stripeConf.timeout});break}default:return log2.warn(`Unknown provider: ${providerName}`),null}if(!provider)return null;return log2.info(`Initialized provider: ${providerName}`),{provider:providerName,providerInstance:provider,webhookSecret}};var init_PaymentService=__esm(()=>{init_Logger2();init_IyzicoProvider();init_StripeProvider()});var exports_Payment={};__export(exports_Payment,{verifyStripeWebhook:()=>verifyStripeWebhook,resolveMarketplaceConfig:()=>resolveMarketplaceConfig,createStripeProvider:()=>createStripeProvider,createPaymentService:()=>createPaymentService,createMarketplaceServices:()=>createMarketplaceServices,createIyzicoProvider:()=>createIyzicoProvider,computeSplitBreakdown:()=>computeSplitBreakdown,MarketplaceService:()=>MarketplaceService,MarketplacePayoutService:()=>MarketplacePayoutService});var init_Payment=__esm(()=>{init_IyzicoProvider();init_Marketplace();init_PaymentService();init_StripeProvider()});import{randomUUID as randomUUID4}from"crypto";class RateLimiter{redis;logger;config;constructor(deps){this.redis=deps.redis,this.logger=deps.logger,this.config=this.mergeConfig(deps.config)}mergeConfig(config){return{enabled:config.enabled??DEFAULT_CONFIG4.enabled,strategy:config.strategy??DEFAULT_CONFIG4.strategy,keyPrefix:config.keyPrefix??DEFAULT_CONFIG4.keyPrefix,failClosed:config.failClosed,authRoutes:{window:config.authRoutes?.window??DEFAULT_CONFIG4.authRoutes.window,max:config.authRoutes?.max??DEFAULT_CONFIG4.authRoutes.max,login:{window:config.authRoutes?.login?.window??DEFAULT_AUTH_LOGIN.window,max:config.authRoutes?.login?.max??DEFAULT_AUTH_LOGIN.max,blockDuration:config.authRoutes?.login?.blockDuration??DEFAULT_AUTH_LOGIN.blockDuration},register:{window:config.authRoutes?.register?.window??DEFAULT_AUTH_REGISTER.window,max:config.authRoutes?.register?.max??DEFAULT_AUTH_REGISTER.max,blockDuration:config.authRoutes?.register?.blockDuration??DEFAULT_AUTH_REGISTER.blockDuration},passwordReset:{window:config.authRoutes?.passwordReset?.window??DEFAULT_AUTH_PASSWORD_RESET.window,max:config.authRoutes?.passwordReset?.max??DEFAULT_AUTH_PASSWORD_RESET.max,blockDuration:config.authRoutes?.passwordReset?.blockDuration??DEFAULT_AUTH_PASSWORD_RESET.blockDuration},magicLink:{window:config.authRoutes?.magicLink?.window??DEFAULT_AUTH_MAGIC_LINK.window,max:config.authRoutes?.magicLink?.max??DEFAULT_AUTH_MAGIC_LINK.max,blockDuration:config.authRoutes?.magicLink?.blockDuration??DEFAULT_AUTH_MAGIC_LINK.blockDuration}},publicRoutes:{window:config.publicRoutes?.window??DEFAULT_CONFIG4.publicRoutes.window,max:config.publicRoutes?.max??DEFAULT_CONFIG4.publicRoutes.max},privateRoutes:{window:config.privateRoutes?.window??DEFAULT_CONFIG4.privateRoutes.window,max:config.privateRoutes?.max??DEFAULT_CONFIG4.privateRoutes.max},byIp:config.byIp??DEFAULT_CONFIG4.byIp,byUserId:config.byUserId??DEFAULT_CONFIG4.byUserId,byEndpoint:config.byEndpoint??DEFAULT_CONFIG4.byEndpoint,skipSuccessfulRequests:config.skipSuccessfulRequests??DEFAULT_CONFIG4.skipSuccessfulRequests,headers:{remaining:config.headers?.remaining??DEFAULT_CONFIG4.headers.remaining,reset:config.headers?.reset??DEFAULT_CONFIG4.headers.reset,limit:config.headers?.limit??DEFAULT_CONFIG4.headers.limit},whitelist:config.whitelist??DEFAULT_CONFIG4.whitelist,blacklist:config.blacklist??DEFAULT_CONFIG4.blacklist}}parseTimeToMs(time){let match=time.match(/^(\d+)(ms|s|m|h|d)$/);if(!match||!match[1]||!match[2])return 60000;let value=parseInt(match[1],10);switch(match[2]){case"ms":return value;case"s":return value*1000;case"m":return value*60*1000;case"h":return value*60*60*1000;case"d":return value*24*60*60*1000;default:return 60000}}buildKey(params){let parts=[this.config.keyPrefix,params.category];if(params.authType&&params.authType!=="other")parts.push(params.authType);if(this.config.byIp&&params.ip)parts.push(`ip:${params.ip}`);if(this.config.byUserId&&params.userId)parts.push(`user:${params.userId}`);if(this.config.byEndpoint&&params.endpoint)parts.push(`ep:${params.endpoint.replace(/\//g,"_")}`);return parts.join(":")}getLimits(category,authType){if(category==="auth"){if(authType&&authType!=="other"){let authConfig=this.config.authRoutes[authType];if(authConfig)return authConfig}return{window:this.config.authRoutes.window,max:this.config.authRoutes.max}}if(category==="public")return this.config.publicRoutes;return this.config.privateRoutes}isWhitelisted(ip){return this.config.whitelist.some((pattern)=>{if(pattern.includes("*"))return new RegExp(`^${pattern.replace(/\*/g,".*")}$`).test(ip);return pattern===ip})}isBlacklisted(ip){return this.config.blacklist.some((pattern)=>{if(pattern.includes("*"))return new RegExp(`^${pattern.replace(/\*/g,".*")}$`).test(ip);return pattern===ip})}async readRedis(key2){let result=await this.redis.read(key2);if(result.success)return result.data;throw new RateLimiterBackendError(result.error?String(result.error):"rate-limit backend read failed")}async check(params){if(!this.config.enabled)return{allowed:!0,remaining:-1,resetAt:0,limit:-1};if(this.isWhitelisted(params.ip))return{allowed:!0,remaining:-1,resetAt:0,limit:-1};if(this.isBlacklisted(params.ip))return this.logger.warn(`[RateLimit] Blacklisted IP: ${params.ip}`),{allowed:!1,remaining:0,resetAt:Date.now()+86400000,limit:0,retryAfter:86400};let key2=this.buildKey(params),limits=this.getLimits(params.category,params.authType),windowMs=this.parseTimeToMs(limits.window),blockKey=`${key2}:blocked`;try{let isBlocked=await this.readRedis(blockKey);if(isBlocked&&isBlocked.until>Date.now()){let retryAfter=Math.ceil((isBlocked.until-Date.now())/1000);return this.logger.warn(`[RateLimit] Blocked: ${key2}, retry after ${retryAfter}s`),{allowed:!1,remaining:0,resetAt:isBlocked.until,limit:limits.max,retryAfter}}if(this.config.strategy==="sliding-window")return await this.slidingWindowCheck(key2,limits.max,windowMs,limits.blockDuration);if(this.config.strategy==="fixed-window")return await this.fixedWindowCheck(key2,limits.max,windowMs,limits.blockDuration);return await this.tokenBucketCheck(key2,limits.max,windowMs)}catch(err){let failClosed=this.config.failClosed===!0||this.config.failClosed===void 0&&params.category==="auth";if(this.logger.error(`[RateLimit] Backend error for ${key2} \u2014 failing ${failClosed?"closed":"open"}`,{error:err instanceof Error?err.message:String(err),category:params.category}),failClosed)return{allowed:!1,remaining:0,resetAt:Date.now()+windowMs,limit:limits.max,retryAfter:Math.ceil(windowMs/1000)};return{allowed:!0,remaining:-1,resetAt:0,limit:limits.max}}}async slidingWindowCheck(key2,max,windowMs,blockDuration){let client=this.redis.getDirectClient?.();if(client)return this.slidingWindowAtomic(client,key2,max,windowMs,blockDuration);let now=Date.now(),windowStart=now-windowMs,dataKey=`${key2}:sw`,timestamps=(await this.readRedis(dataKey))?.timestamps||[];timestamps=timestamps.filter((ts)=>ts>windowStart);let count=timestamps.length,firstTimestamp=timestamps[0],resetAt=firstTimestamp!==void 0?firstTimestamp+windowMs:now+windowMs;if(count>=max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return timestamps.push(now),await this.redis.create(dataKey,{timestamps},Math.ceil(windowMs/1000)+1),{allowed:!0,remaining:max-timestamps.length,resetAt,limit:max}}async slidingWindowAtomic(client,key2,max,windowMs,blockDuration){let now=Date.now(),member=`${now}-${randomUUID4()}`,res=await client.eval(SLIDING_WINDOW_LUA,1,`${key2}:swz`,String(now),String(windowMs),String(max),member),allowed=Number(res?.[0])===1,count=Number(res?.[1]??0),resetAt=now+windowMs;if(!allowed){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil(windowMs/1000)}}return{allowed:!0,remaining:Math.max(0,max-count),resetAt,limit:max}}async fixedWindowAtomic(client,key2,max,windowMs,blockDuration){let now=Date.now(),windowId=Math.floor(now/windowMs),dataKey=`${key2}:fw:${windowId}`,resetAt=(windowId+1)*windowMs,count=await client.incr(dataKey);if(count===1)await client.pexpire(dataKey,windowMs+1000);if(count>max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return{allowed:!0,remaining:Math.max(0,max-count),resetAt,limit:max}}async fixedWindowCheck(key2,max,windowMs,blockDuration){let client=this.redis.getDirectClient?.();if(client)return this.fixedWindowAtomic(client,key2,max,windowMs,blockDuration);let now=Date.now(),windowId=Math.floor(now/windowMs),dataKey=`${key2}:fw:${windowId}`,resetAt=(windowId+1)*windowMs,count=(await this.readRedis(dataKey))?.count||0;if(count>=max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return await this.redis.create(dataKey,{count:count+1},Math.ceil(windowMs/1000)+1),{allowed:!0,remaining:max-(count+1),resetAt,limit:max}}async tokenBucketCheck(key2,max,windowMs){let now=Date.now(),refillRate=max/windowMs,dataKey=`${key2}:tb`,data=await this.readRedis(dataKey),tokens=data?.tokens??max,lastRefill=data?.lastRefill??now,refill=(now-lastRefill)*refillRate;if(tokens=Math.min(max,tokens+refill),tokens<1){let waitTime=Math.ceil((1-tokens)/refillRate);return{allowed:!1,remaining:0,resetAt:now+waitTime,limit:max,retryAfter:Math.ceil(waitTime/1000)}}return tokens-=1,await this.redis.create(dataKey,{tokens,lastRefill:now},Math.ceil(windowMs/1000)*2),{allowed:!0,remaining:Math.floor(tokens),resetAt:now+windowMs,limit:max}}async decrement(params){if(!this.config.skipSuccessfulRequests)return;let key2=this.buildKey(params);if(this.config.strategy==="sliding-window"){let dataKey=`${key2}:sw`,data=await this.readRedis(dataKey);if(data?.timestamps?.length){data.timestamps.pop();let windowMs=this.parseTimeToMs(this.getLimits(params.category,params.authType).window);await this.redis.create(dataKey,data,Math.ceil(windowMs/1000)+1)}}else if(this.config.strategy==="fixed-window"){let windowMs=this.parseTimeToMs(this.getLimits(params.category,params.authType).window),windowId=Math.floor(Date.now()/windowMs),dataKey=`${key2}:fw:${windowId}`,data=await this.readRedis(dataKey);if(data?.count)await this.redis.create(dataKey,{count:data.count-1},Math.ceil(windowMs/1000)+1)}}getHeaders(result){let headers={};return headers[this.config.headers.remaining]=String(result.remaining),headers[this.config.headers.reset]=String(Math.ceil(result.resetAt/1000)),headers[this.config.headers.limit]=String(result.limit),headers}isEnabled(){return this.config.enabled}}var SLIDING_WINDOW_LUA=`
89
+ ${params.body}`:`*${params.title}*`,timeoutMs=telegramConfig?.timeoutMs??DEFAULT_DELIVERY_TIMEOUT_MS;try{let response=await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:chatId,text,parse_mode:"Markdown"}),signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){let errBody=await response.text().catch(()=>"");this.logger.error(`[Notification] Telegram delivery failed (${response.status}): ${errBody.slice(0,300)}`);return}this.logger.debug(`[Notification] Telegram notification sent: ${params.title}`)}catch(error){this.logger.error(`[Notification] Telegram delivery error: ${error.message}`)}}async sendWebhookNotification(params){let webhookConfig=this.config.channels?.webhook,url=resolveEnvValue2(webhookConfig?.url);if(!url){this.logger.warn("[Notification] Webhook channel enabled but url is missing or unresolved \u2014 skipping delivery");return}let headers={"Content-Type":"application/json"};for(let[key,value]of Object.entries(webhookConfig?.headers||{})){let resolved=resolveEnvValue2(value);if(resolved)headers[key]=resolved}let timeoutMs=webhookConfig?.timeoutMs??DEFAULT_DELIVERY_TIMEOUT_MS;try{let response=await fetch(url,{method:"POST",headers,body:JSON.stringify({user_id:params.user_id,title:params.title,body:params.body??null,entity_name:params.entity_name??null,entity_id:params.entity_id??null,type:params.type??"system",source:params.source??null,timestamp:new Date().toISOString()}),signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){let errBody=await response.text().catch(()=>"");this.logger.error(`[Notification] Webhook delivery failed (${response.status}): ${errBody.slice(0,300)}`);return}this.logger.debug(`[Notification] Webhook notification sent: ${params.title}`)}catch(error){this.logger.error(`[Notification] Webhook delivery error: ${error.message}`)}}async sendEmailNotification(userId,title,body){if(!this.emailService?.isAvailable()){this.logger.warn("[Notification] Email service not available for email notification");return}let usersTable=this.getTable("users");if(!usersTable){this.logger.warn("[Notification] users table not found");return}let user=(await this.db.select({email:this.getCol(usersTable,"email")}).from(usersTable).where(eq9(this.getCol(usersTable,"id"),userId)).limit(1))[0];if(!user?.email){this.logger.warn(`[Notification] No email found for user ${userId}`);return}await this.emailService.sendEmail({to:user.email,subject:title,html:body||title}),this.logger.debug(`[Notification] Email notification sent to ${user.email}: ${title}`)}async getNotifications(userId,options){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return[];let conditions=[eq9(this.getCol(notificationsTable,"userId"),userId)];if(options?.type)conditions.push(eq9(this.getCol(notificationsTable,"type"),options.type));return(await this.db.select().from(notificationsTable).where(and4(...conditions)).orderBy(desc(this.getCol(notificationsTable,"createdAt"))).limit(options?.limit||50).offset(options?.offset||0)).map((r)=>fromCamel(r))}async getUnseenCount(userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return 0;return(await this.db.select().from(notificationsTable).where(and4(eq9(this.getCol(notificationsTable,"userId"),userId),eq9(this.getCol(notificationsTable,"isSeen"),!1)))).length}async markAsSeen(notificationId,userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return!1;return await this.db.update(notificationsTable).set(toCamel({is_seen:!0,seen_at:new Date})).where(and4(eq9(this.getCol(notificationsTable,"id"),notificationId),eq9(this.getCol(notificationsTable,"userId"),userId))),!0}async markAllAsSeen(userId){let notificationsTable=this.getTable("notifications");if(!notificationsTable)return 0;return(await this.db.update(notificationsTable).set(toCamel({is_seen:!0,seen_at:new Date})).where(and4(eq9(this.getCol(notificationsTable,"userId"),userId),eq9(this.getCol(notificationsTable,"isSeen"),!1))).returning()).length}}var DEFAULT_DELIVERY_TIMEOUT_MS=1e4;var init_Notification=__esm(()=>{init_types4()});function buildGenericAuthUrl(config,state){if(!config.authorizationUrl)throw Error("Generic OAuth provider requires authorizationUrl");let scopes2=config.scopes??[],params=new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",state,...scopes2.length>0?{scope:scopes2.join(" ")}:{},...config.extraAuthParams});return`${config.authorizationUrl}?${params.toString()}`}async function exchangeGenericCode(code,config){if(!config.tokenUrl)throw Error("Generic OAuth provider requires tokenUrl");let tokenRes=await fetch(config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`Generic OAuth token exchange failed: ${err}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`OAuth error: ${tokenData.error_description||tokenData.error}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope};if(!config.userInfoUrl)return{profile:{providerAccountId:tokenData.access_token,rawProfile:tokenData},tokens};let userRes=await fetch(config.userInfoUrl,{headers:{Authorization:`Bearer ${tokenData.access_token}`}});if(!userRes.ok)throw Error("Failed to fetch user info from generic OAuth provider");let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.id??rawProfile.sub??rawProfile.user_id??tokenData.access_token,email:rawProfile.email,emailVerified:rawProfile.email_verified===!0||rawProfile.email_verified==="true",name:rawProfile.name??rawProfile.display_name??rawProfile.username,avatarUrl:rawProfile.avatar_url??rawProfile.picture??rawProfile.photo,rawProfile},tokens}}function buildGithubAuthUrl(config,state){let scopes2=config.scopes??["read:user","user:email"];return`https://github.com/login/oauth/authorize?${new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,scope:scopes2.join(" "),state,...config.extraAuthParams}).toString()}`}async function exchangeGithubCode(code,config){let tokenRes=await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`GitHub token exchange failed: ${err}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`GitHub OAuth error: ${tokenData.error_description||tokenData.error}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes=await fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${tokenData.access_token}`,Accept:"application/vnd.github+json"}});if(!userRes.ok)throw Error("Failed to fetch GitHub user info");let rawProfile=await userRes.json(),email=rawProfile.email,emailVerified=!!email;if(!email)try{let emailsRes=await fetch("https://api.github.com/user/emails",{headers:{Authorization:`Bearer ${tokenData.access_token}`,Accept:"application/vnd.github+json"}});if(emailsRes.ok){let emails=await emailsRes.json(),primary=emails.find((e)=>e.primary&&e.verified);if(primary)email=primary.email,emailVerified=!0;else email=emails[0]?.email,emailVerified=!1}}catch{}return{profile:{providerAccountId:String(rawProfile.id),email,emailVerified,name:rawProfile.name??rawProfile.login,avatarUrl:rawProfile.avatar_url,rawProfile},tokens}}function buildGoogleAuthUrl(config,state){let scopes2=config.scopes??["openid","email","profile"];return`https://accounts.google.com/o/oauth2/v2/auth?${new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",scope:scopes2.join(" "),state,access_type:"offline",prompt:"select_account",...config.extraAuthParams}).toString()}`}async function exchangeGoogleCode(code,config){let tokenRes=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()});if(!tokenRes.ok){let err=await tokenRes.text();throw Error(`Google token exchange failed: ${err}`)}let tokenData=await tokenRes.json(),tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes=await fetch("https://www.googleapis.com/oauth2/v3/userinfo",{headers:{Authorization:`Bearer ${tokenData.access_token}`}});if(!userRes.ok)throw Error("Failed to fetch Google user info");let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.sub,email:rawProfile.email,emailVerified:rawProfile.email_verified===!0||rawProfile.email_verified==="true",name:rawProfile.name,avatarUrl:rawProfile.picture,rawProfile},tokens}}function getMicrosoftBaseUrl(config){return`https://login.microsoftonline.com/${config.tenantId??"common"}/oauth2/v2.0`}function describeFetchError(err){if(err&&typeof err==="object"){let e=err,cause=e.cause,parts=[];if(e.message)parts.push(`message=${e.message}`);if(e.code)parts.push(`code=${e.code}`);if(cause?.code)parts.push(`cause.code=${cause.code}`);if(cause?.name)parts.push(`cause.name=${cause.name}`);if(cause?.message)parts.push(`cause.message=${cause.message}`);return parts.join(" | ")||String(err)}return String(err)}function buildMicrosoftAuthUrl(config,state){let scopes2=config.scopes??["openid","email","profile","User.Read"],params=new URLSearchParams({client_id:config.clientId,redirect_uri:config.redirectUri,response_type:"code",scope:scopes2.join(" "),state,response_mode:"query",prompt:"select_account",...config.extraAuthParams});return`${getMicrosoftBaseUrl(config)}/authorize?${params.toString()}`}async function exchangeMicrosoftCode(code,config){let tokenUrl=`${getMicrosoftBaseUrl(config)}/token`,tokenRes;try{tokenRes=await fetch(tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code,client_id:config.clientId,client_secret:config.clientSecret,redirect_uri:config.redirectUri,grant_type:"authorization_code"}).toString()})}catch(err){throw Error(`Microsoft token endpoint fetch failed | url=${tokenUrl} | redirect_uri=${config.redirectUri} | ${describeFetchError(err)}`,{cause:err})}if(!tokenRes.ok){let body=await tokenRes.text().catch(()=>"<unable to read body>");throw Error(`Microsoft token exchange failed | status=${tokenRes.status} | url=${tokenUrl} | redirect_uri=${config.redirectUri} | body=${body}`)}let tokenData=await tokenRes.json();if(tokenData.error)throw Error(`Microsoft OAuth error: ${tokenData.error_description||tokenData.error} | url=${tokenUrl}`);let tokens={accessToken:tokenData.access_token,refreshToken:tokenData.refresh_token,expiresAt:tokenData.expires_in?new Date(Date.now()+tokenData.expires_in*1000):void 0,scope:tokenData.scope},userRes;try{userRes=await fetch("https://graph.microsoft.com/v1.0/me",{headers:{Authorization:`Bearer ${tokenData.access_token}`}})}catch(err){throw Error(`Microsoft userinfo fetch failed | url=https://graph.microsoft.com/v1.0/me | ${describeFetchError(err)}`,{cause:err})}if(!userRes.ok){let body=await userRes.text().catch(()=>"<unable to read body>");throw Error(`Microsoft userinfo failed | status=${userRes.status} | url=https://graph.microsoft.com/v1.0/me | body=${body}`)}let rawProfile=await userRes.json();return{profile:{providerAccountId:rawProfile.id,email:rawProfile.mail??rawProfile.userPrincipalName,emailVerified:!0,name:rawProfile.displayName,avatarUrl:void 0,rawProfile},tokens}}var exports_stateStore={};__export(exports_stateStore,{createRedisOAuthStateStore:()=>createRedisOAuthStateStore,InMemoryOAuthStateStore:()=>InMemoryOAuthStateStore});class InMemoryOAuthStateStore{store=new Map;cleanupInterval=null;constructor(cleanupEverySeconds){this.cleanupInterval=setInterval(()=>{let now=Date.now();for(let[k,v]of this.store.entries())if(v.expiresAt<now)this.store.delete(k)},cleanupEverySeconds*1000),this.cleanupInterval.unref?.()}async set(state,payload,ttlSeconds){this.store.set(state,{payload,expiresAt:Date.now()+ttlSeconds*1000})}async consume(state){let entry=this.store.get(state);if(!entry)return null;if(this.store.delete(state),entry.expiresAt<Date.now())return null;return entry.payload}stop(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}}var key=(state)=>`nucleus:oauth:state:${state}`,createRedisOAuthStateStore=(redis)=>({async set(state,payload,ttlSeconds){await redis.create(key(state),payload,ttlSeconds)},async consume(state){let res=await redis.read(key(state));if(!res.success||!res.data)return null;return await redis.remove(key(state)),res.data}});import{randomBytes as randomBytes3}from"crypto";class OAuthService{config;stateStore;constructor(config,stateStore){this.config=config,this.stateStore=stateStore??new InMemoryOAuthStateStore(config.stateTtlSeconds??600)}stop(){this.stateStore.stop?.()}isProviderEnabled(provider){return!!(this.config.enabled&&this.config.providers[provider])}getEnabledProviders(){if(!this.config.enabled)return[];return Object.keys(this.config.providers)}async buildAuthorizationUrl(provider,linkUserId,redirectUrl){let providerConfig=this.config.providers[provider];if(!providerConfig)throw Error(`OAuth provider "${provider}" is not configured`);let statePayload={provider,linkUserId,redirectUrl,createdAt:Date.now()},state=randomBytes3(32).toString("hex"),ttlSeconds=this.config.stateTtlSeconds??600;switch(await this.stateStore.set(state,statePayload,ttlSeconds),provider){case"google":return buildGoogleAuthUrl(providerConfig,state);case"github":return buildGithubAuthUrl(providerConfig,state);case"microsoft":return buildMicrosoftAuthUrl(providerConfig,state);default:return buildGenericAuthUrl(providerConfig,state)}}consumeState(state){return this.stateStore.consume(state)}async exchangeCode(provider,code){let providerConfig=this.config.providers[provider];if(!providerConfig)throw Error(`OAuth provider "${provider}" is not configured`);switch(provider){case"google":return exchangeGoogleCode(code,providerConfig);case"github":return exchangeGithubCode(code,providerConfig);case"microsoft":return exchangeMicrosoftCode(code,providerConfig);default:return exchangeGenericCode(code,providerConfig)}}get allowAccountLinking(){return this.config.allowAccountLinking??!0}get autoCreateUser(){return this.config.autoCreateUser??!0}get successRedirectUrl(){return this.config.successRedirectUrl??"/"}get errorRedirectUrl(){return this.config.errorRedirectUrl??"/login"}get sendInviteOnCreate(){return this.config.sendInviteOnCreate??!1}get basePath(){return this.config.basePath??"/auth/oauth"}}var init_OAuthService=()=>{};var init_OAuth=__esm(()=>{init_OAuthService()});import*as crypto5 from"crypto";var IYZICO_PATHS,generateAuthString=(apiKey,secretKey,path2,payload,randomKey)=>{let compound=payload?randomKey+path2+payload:randomKey+path2,encryptedData=crypto5.createHmac("sha256",secretKey).update(compound).digest("hex"),authorizationString=`apiKey:${apiKey}&randomKey:${randomKey}&signature:${encryptedData}`;return`IYZWSv2 ${Buffer.from(authorizationString).toString("base64")}`},buildRequest=(config,path2,payload)=>{let body=JSON.stringify(payload),randomKey=Date.now().toString(32),headers=new Headers;return headers.set("Content-Type","application/json"),headers.set("x-iyzi-rnd",randomKey),headers.set("Authorization",generateAuthString(config.apiKey,config.secretKey,path2,body,randomKey)),{url:new URL(`${config.apiUrl}${path2}`).toString(),headers,body}},iyzicoFetch=async(config,path2,payload)=>{let{url,headers,body}=buildRequest(config,path2,payload);try{let json=await(await fetch(url,{method:"POST",headers,body})).json(),isSuccess=json.status==="success";return{success:isSuccess,data:isSuccess?json:null,errorCode:json.errorCode??void 0,errorMessage:json.errorMessage??void 0,rawResponse:json}}catch(error){let message=error instanceof Error?error.message:"Unknown fetch error";return{success:!1,data:null,errorCode:"NETWORK_ERROR",errorMessage:message,rawResponse:{error:message}}}},createIyzicoProvider=(config)=>{let generateConversationId=()=>`nk-${Date.now().toString(16)}`;return{get name(){return"iyzico"},async initialize3DS(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId,price:request.price,paidPrice:request.paidPrice,currency:request.currency,installment:request.installment??1,paymentChannel:request.paymentChannel??"WEB",basketId:request.basketId??Date.now().toString(16),paymentGroup:request.paymentGroup??"PRODUCT",paymentCard:request.paymentCard,buyer:request.buyer,shippingAddress:request.shippingAddress,billingAddress:request.billingAddress,basketItems:request.basketItems,callbackUrl:request.callbackUrl},result=await iyzicoFetch(config,IYZICO_PATHS.INITIALIZE_3DS,payload);return{success:result.success,threeDSHtmlContent:result.data?.threeDSHtmlContent,paymentId:result.data?.paymentId,signature:result.data?.signature,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async complete3DS(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId,paymentId:request.paymentId},result=await iyzicoFetch(config,IYZICO_PATHS.COMPLETE_3DS,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,paidPrice:result.data?.paidPrice,currency:result.data?.currency,installment:result.data?.installment,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,binNumber:result.data?.binNumber,lastFourDigits:result.data?.lastFourDigits,basketId:result.data?.basketId,authCode:result.data?.authCode,signature:result.data?.signature,fraudStatus:result.data?.fraudStatus,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async getPaymentDetail(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),paymentId:request.paymentId,paymentConversationId:request.paymentConversationId},result=await iyzicoFetch(config,IYZICO_PATHS.PAYMENT_DETAIL,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,paidPrice:result.data?.paidPrice,currency:result.data?.currency,basketId:result.data?.basketId,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,lastFourDigits:result.data?.lastFourDigits,binNumber:result.data?.binNumber,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async queryBin(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),binNumber:request.binNumber.slice(0,6)};if(request.price!==void 0)payload.price=request.price;let result=await iyzicoFetch(config,IYZICO_PATHS.BIN_CHECK,payload);return{success:result.success,binNumber:result.data?.binNumber,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,bankName:result.data?.bankName,bankCode:result.data?.bankCode,commercial:result.data?.commercial,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async saveCard(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),card:request.card,email:request.email,externalId:request.externalId},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_SAVE,payload);return{success:result.success,cardUserKey:result.data?.cardUserKey,cardToken:result.data?.cardToken,cardAlias:result.data?.cardAlias,binNumber:result.data?.binNumber,lastFourDigits:result.data?.lastFourDigits,cardType:result.data?.cardType,cardAssociation:result.data?.cardAssociation,cardFamily:result.data?.cardFamily,cardBankName:result.data?.cardBankName,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async listCards(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),cardUserKey:request.cardUserKey},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_LIST,payload),cards=(result.data?.cardDetails??[]).map((cd)=>({cardToken:cd.cardToken,cardAlias:cd.cardAlias,binNumber:cd.binNumber,lastFourDigits:cd.lastFourDigits,cardType:cd.cardType,cardAssociation:cd.cardAssociation,cardFamily:cd.cardFamily,cardBankName:cd.cardBankName,cardBankCode:cd.cardBankCode}));return{success:result.success,cardUserKey:result.data?.cardUserKey,cards,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async deleteCard(request){let payload={locale:request.locale??"tr",conversationId:generateConversationId(),cardUserKey:request.cardUserKey,cardToken:request.cardToken},result=await iyzicoFetch(config,IYZICO_PATHS.CARD_DELETE,payload);return{success:result.success,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async refund(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),paymentTransactionId:request.paymentTransactionId,price:request.price,currency:request.currency??"TRY"};if(request.ip)payload.ip=request.ip;let result=await iyzicoFetch(config,IYZICO_PATHS.REFUND,payload);return{success:result.success,paymentId:result.data?.paymentId,price:result.data?.price,currency:result.data?.currency,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},parseCallback(formData){return{success:formData.mdStatus==="1",paymentId:formData.paymentId??"",conversationId:formData.conversationId??"",mdStatus:formData.mdStatus??"",rawData:formData}},async registerSubMerchant(request){let typeMap={personal:"PERSONAL",private_company:"PRIVATE_COMPANY",limited_or_joint_stock_company:"LIMITED_OR_JOINT_STOCK_COMPANY"},payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantExternalId:request.subMerchantExternalId,subMerchantType:typeMap[request.subMerchantType]??"PERSONAL",name:request.name,email:request.email,gsmNumber:request.gsmNumber,address:request.address,iban:request.iban,contactName:request.contactName,contactSurname:request.contactSurname,identityNumber:request.identityNumber,currency:request.currency??"TRY"};if(request.subMerchantType==="private_company")payload.taxOffice=request.taxOffice,payload.legalCompanyTitle=request.legalCompanyTitle;if(request.subMerchantType==="limited_or_joint_stock_company")payload.taxOffice=request.taxOffice,payload.taxNumber=request.taxNumber,payload.legalCompanyTitle=request.legalCompanyTitle;let result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_CREATE,payload);return{success:result.success,subMerchantKey:result.data?.subMerchantKey,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async updateSubMerchant(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantKey:request.subMerchantKey};if(request.name)payload.name=request.name;if(request.email)payload.email=request.email;if(request.gsmNumber)payload.gsmNumber=request.gsmNumber;if(request.address)payload.address=request.address;if(request.iban)payload.iban=request.iban;if(request.contactName)payload.contactName=request.contactName;if(request.contactSurname)payload.contactSurname=request.contactSurname;if(request.identityNumber)payload.identityNumber=request.identityNumber;if(request.taxOffice)payload.taxOffice=request.taxOffice;if(request.taxNumber)payload.taxNumber=request.taxNumber;if(request.legalCompanyTitle)payload.legalCompanyTitle=request.legalCompanyTitle;if(request.currency)payload.currency=request.currency;let result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_UPDATE,payload);return{success:result.success,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async getSubMerchant(request){let payload={locale:request.locale??"tr",conversationId:request.conversationId??generateConversationId(),subMerchantExternalId:request.subMerchantExternalId},result=await iyzicoFetch(config,IYZICO_PATHS.SUB_MERCHANT_DETAIL,payload);return{success:result.success,subMerchantKey:result.data?.subMerchantKey,name:result.data?.name,email:result.data?.email,gsmNumber:result.data?.gsmNumber,address:result.data?.address,iban:result.data?.iban,subMerchantType:result.data?.subMerchantType,currency:result.data?.currency,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async approveSplit(request){let payload={locale:request.locale??"tr",paymentTransactionId:request.paymentTransactionId},result=await iyzicoFetch(config,IYZICO_PATHS.APPROVAL,payload);return{success:result.success,paymentTransactionId:result.data?.paymentTransactionId,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async disapproveSplit(request){let payload={locale:request.locale??"tr",paymentTransactionId:request.paymentTransactionId},result=await iyzicoFetch(config,IYZICO_PATHS.DISAPPROVAL,payload);return{success:result.success,paymentTransactionId:result.data?.paymentTransactionId,errorCode:result.errorCode,errorMessage:result.errorMessage,rawResponse:result.rawResponse}},async createProduct(request){let localId=`iyzico-prod-${Date.now().toString(36)}`;return{success:!0,providerProductId:localId,name:request.name,rawResponse:{local:!0,id:localId}}},async updateProduct(request){return{success:!0,providerProductId:request.providerProductId,rawResponse:{local:!0,updated:request.providerProductId}}},async getProduct(request){return{success:!0,providerProductId:request.providerProductId,rawResponse:{local:!0,message:"Product data is stored locally for iyzico"}}},async listProducts(_request){return{success:!0,products:[],hasMore:!1,rawResponse:{local:!0,message:"Products are stored locally for iyzico"}}},async archiveProduct(request){return{success:!0,rawResponse:{local:!0,archived:request.providerProductId}}},async createPrice(request){let localId=`iyzico-price-${Date.now().toString(36)}`;return{success:!0,providerPriceId:localId,rawResponse:{local:!0,id:localId,product:request.providerProductId}}},async updatePrice(request){return{success:!0,providerPriceId:request.providerPriceId,rawResponse:{local:!0,updated:request.providerPriceId}}},async getPrice(request){return{success:!0,providerPriceId:request.providerPriceId,rawResponse:{local:!0,message:"Price data is stored locally for iyzico"}}},async listPrices(_request){return{success:!0,prices:[],hasMore:!1,rawResponse:{local:!0,message:"Prices are stored locally for iyzico"}}},async archivePrice(request){return{success:!0,rawResponse:{local:!0,archived:request.providerPriceId}}},async createCustomer(request){return{success:!0,providerCustomerId:`local_cus_${Date.now()}`,email:request.email,rawResponse:{local:!0,message:"Customer stored locally for iyzico"}}},async updateCustomer(request){return{success:!0,providerCustomerId:request.providerCustomerId,rawResponse:{local:!0,message:"Customer updated locally for iyzico"}}},async getCustomer(request){return{success:!0,providerCustomerId:request.providerCustomerId,rawResponse:{local:!0,message:"Customer data is stored locally for iyzico"}}},async listCustomers(_request){return{success:!0,customers:[],hasMore:!1,rawResponse:{local:!0,message:"Customers are stored locally for iyzico"}}},async deleteCustomer(_request){return{success:!0,rawResponse:{local:!0,message:"Customer deleted locally for iyzico"}}},async createSubscription(_request){return{success:!0,providerSubscriptionId:`local_sub_${Date.now()}`,status:"active",rawResponse:{local:!0,message:"Subscription stored locally for iyzico"}}},async updateSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,status:"active",rawResponse:{local:!0,message:"Subscription updated locally for iyzico"}}},async cancelSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,status:"canceled",rawResponse:{local:!0,message:"Subscription canceled locally for iyzico"}}},async getSubscription(request){return{success:!0,providerSubscriptionId:request.providerSubscriptionId,rawResponse:{local:!0,message:"Subscription data is stored locally for iyzico"}}},async listSubscriptions(_request){return{success:!0,subscriptions:[],hasMore:!1,rawResponse:{local:!0,message:"Subscriptions are stored locally for iyzico"}}},async reportUsage(request){return{success:!0,usageRecordId:`local_ur_${Date.now()}`,quantity:request.quantity,timestamp:request.timestamp??Math.floor(Date.now()/1000),rawResponse:{local:!0,message:"Usage recorded locally for iyzico"}}},async listUsageRecordSummaries(_request){return{success:!0,summaries:[],hasMore:!1,rawResponse:{local:!0,message:"Usage summaries are stored locally for iyzico"}}},async createInvoice(_request){return{success:!0,providerInvoiceId:`local_inv_${Date.now()}`,status:"draft",rawResponse:{local:!0,message:"Invoice stored locally for iyzico"}}},async finalizeInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,status:"open",rawResponse:{local:!0,message:"Invoice finalized locally for iyzico"}}},async sendInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice send is a no-op for iyzico"}}},async voidInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice voided locally for iyzico"}}},async payInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,status:"paid",rawResponse:{local:!0,message:"Invoice payment is a no-op for iyzico"}}},async getInvoice(request){return{success:!0,providerInvoiceId:request.providerInvoiceId,rawResponse:{local:!0,message:"Invoice data is stored locally for iyzico"}}},async listInvoices(_request){return{success:!0,invoices:[],hasMore:!1,rawResponse:{local:!0,message:"Invoices are stored locally for iyzico"}}},async getBalance(){return{success:!0,available:[],pending:[],rawResponse:{local:!0,message:"Balance is not tracked by iyzico provider"}}},async createPayout(_request){return{success:!0,providerPayoutId:`local_po_${Date.now()}`,status:"pending",rawResponse:{local:!0,message:"Payout created locally for iyzico"}}},async getPayout(request){return{success:!0,providerPayoutId:request.providerPayoutId,rawResponse:{local:!0,message:"Payout data is stored locally for iyzico"}}},async cancelPayout(request){return{success:!0,providerPayoutId:request.providerPayoutId,status:"canceled",rawResponse:{local:!0,message:"Payout cancelled locally for iyzico"}}},async listPayouts(_request){return{success:!0,payouts:[],hasMore:!1,rawResponse:{local:!0,message:"Payouts are stored locally for iyzico"}}},async createTransfer(_request){return{success:!0,providerTransferId:`local_tr_${Date.now()}`,rawResponse:{local:!0,message:"Transfer created locally for iyzico"}}},async listTransfers(_request){return{success:!0,transfers:[],hasMore:!1,rawResponse:{local:!0,message:"Transfers are stored locally for iyzico"}}}}};var init_IyzicoProvider=__esm(()=>{IYZICO_PATHS={INITIALIZE_3DS:"/payment/3dsecure/initialize",COMPLETE_3DS:"/payment/3dsecure/auth",PAYMENT_AUTH:"/payment/auth",PAYMENT_DETAIL:"/payment/detail",BIN_CHECK:"/payment/bin/check",CARD_SAVE:"/cardstorage/card",CARD_LIST:"/cardstorage/cards",CARD_DELETE:"/cardstorage/card/delete",REFUND:"/payment/refund",SUB_MERCHANT_CREATE:"/onboarding/submerchant",SUB_MERCHANT_UPDATE:"/onboarding/submerchant",SUB_MERCHANT_DETAIL:"/onboarding/submerchant/detail",APPROVAL:"/payment/iyzipos/item/approve",DISAPPROVAL:"/payment/iyzipos/item/disapprove"}});var resolveMarketplaceConfig=(payment)=>{let m=payment?.marketplace;return{enabled:m?.enabled===!0,defaultSettlementDelayDays:m?.defaultSettlementDelayDays??7,newSellerDelayDays:m?.newSellerDelayDays??14,reserveRate:clampRate(m?.reserveRate??0),reserveReleaseDays:m?.reserveReleaseDays??0,minimumPayoutAmount:m?.minimumPayoutAmount??0,defaultCurrency:m?.defaultCurrency??payment?.defaultCurrency??"USD",autoReleaseReserves:m?.autoReleaseReserves===!0}},clampRate=(rate)=>{if(Number.isNaN(rate)||rate<0)return 0;if(rate>1)return 1;return rate},addDays=(base,days)=>new Date(base.getTime()+days*24*60*60*1000),computeSplitBreakdown=(input,config,policy,now=new Date)=>{let providerFee=Math.max(0,Math.round(input.providerFeeAmount??0)),platformFee=Math.max(0,Math.round(input.platformFeeAmount)),afterFees=Math.max(0,input.grossAmount-providerFee-platformFee),reserveRate=input.reserveRate??policy?.reserveRate??config.reserveRate,reserveAmount=Math.min(afterFees,Math.floor(afterFees*clampRate(reserveRate))),netPayable=afterFees-reserveAmount,delayDays=input.settlementDelayDays??(input.isNewSeller?policy?.newSellerDelayDays??config.newSellerDelayDays:policy?.defaultDelayDays??config.defaultSettlementDelayDays);return{grossAmount:input.grossAmount,providerFeeAmount:providerFee,platformFeeAmount:platformFee,reserveAmount,netPayableAmount:netPayable,availableForPayoutAt:addDays(now,Math.max(0,delayDays))}},str2=(v)=>v==null?"":String(v),strOrNull2=(v)=>v==null?null:String(v),num=(v)=>{let n=typeof v==="number"?v:Number(v);return Number.isFinite(n)?n:0},rowToSplit=(row)=>({id:str2(row.id),transactionId:strOrNull2(row.transactionId??row.transaction_id),recipientOwnerType:str2(row.recipientOwnerType??row.recipient_owner_type)||"seller",recipientOwnerId:str2(row.recipientOwnerId??row.recipient_owner_id),provider:str2(row.provider),grossAmount:num(row.grossAmount??row.gross_amount),providerFeeAmount:num(row.providerFeeAmount??row.provider_fee_amount),platformFeeAmount:num(row.platformFeeAmount??row.platform_fee_amount),reserveAmount:num(row.reserveAmount??row.reserve_amount),netPayableAmount:num(row.netPayableAmount??row.net_payable_amount),currency:str2(row.currency),availableForPayoutAt:strOrNull2(row.availableForPayoutAt??row.available_for_payout_at),status:str2(row.status)||"pending",sourceType:strOrNull2(row.sourceType??row.source_type),sourceId:strOrNull2(row.sourceId??row.source_id)}),rowToPayoutRequest=(row)=>({id:str2(row.id),recipientOwnerType:str2(row.recipientOwnerType??row.recipient_owner_type)||"seller",recipientOwnerId:str2(row.recipientOwnerId??row.recipient_owner_id),amount:num(row.amount),currency:str2(row.currency),status:str2(row.status)||"pending",provider:strOrNull2(row.provider),providerPayoutId:strOrNull2(row.providerPayoutId??row.provider_payout_id),providerTransferId:strOrNull2(row.providerTransferId??row.provider_transfer_id),destination:strOrNull2(row.destination),failureReason:strOrNull2(row.failureReason??row.failure_reason),requestedBy:strOrNull2(row.requestedBy??row.requested_by)}),rowToSettlementPolicy=(row)=>({id:str2(row.id),ownerType:str2(row.ownerType??row.owner_type),ownerId:str2(row.ownerId??row.owner_id),defaultDelayDays:num(row.defaultDelayDays??row.default_delay_days),newSellerDelayDays:num(row.newSellerDelayDays??row.new_seller_delay_days),reserveRate:num(row.reserveRate??row.reserve_rate),reserveDays:num(row.reserveDays??row.reserve_days),minimumPayoutAmount:num(row.minimumPayoutAmount??row.minimum_payout_amount),currency:strOrNull2(row.currency),earlyPayoutEnabled:Boolean(row.earlyPayoutEnabled??row.early_payout_enabled),status:str2(row.status)||"active"}),rowToDispute=(row)=>({id:str2(row.id),transactionId:strOrNull2(row.transactionId??row.transaction_id),provider:str2(row.provider),providerDisputeId:strOrNull2(row.providerDisputeId??row.provider_dispute_id),ownerType:strOrNull2(row.ownerType??row.owner_type),ownerId:strOrNull2(row.ownerId??row.owner_id),amount:row.amount==null?null:num(row.amount),currency:strOrNull2(row.currency),reason:strOrNull2(row.reason),status:str2(row.status)||"open",resolvedAt:strOrNull2(row.resolvedAt??row.resolved_at)});import{and as and5,eq as eq10,or,sql as sql2}from"drizzle-orm";class MarketplacePayoutService{getDb;provider;logger;t;marketplace;constructor(cfg,marketplace){this.getDb=cfg.getDb,this.provider=cfg.provider,this.logger=cfg.logger.scoped("payment.payout"),this.t=cfg.tables,this.marketplace=marketplace}table(name){return this.t[name]}async requestPayout(input){let db=this.getDb();if(!db)throw Error("Marketplace payouts not ready");let recipientType=input.recipientOwnerType??"seller",currency=input.currency??this.marketplace.config.defaultCurrency;if(input.amount<=0)throw Error("Payout amount must be positive");let minimum=this.marketplace.config.minimumPayoutAmount;if(input.amount<minimum)throw Error(`Payout amount is below the minimum of ${minimum}`);let table=this.table("payoutRequests"),lockKey=`payout:${recipientType}:${input.recipientOwnerId}:${currency}`,insertRow=async(executor,locked)=>{let balance=await this.marketplace.getBalance(recipientType,input.recipientOwnerId,currency,locked);if(input.amount>balance.available)throw Error(`Insufficient available balance: requested ${input.amount}, available ${balance.available}`);let rows=await executor.insert(table).values({recipientOwnerType:recipientType,recipientOwnerId:input.recipientOwnerId,amount:input.amount,currency,status:"pending",destination:input.destination??null,requestedBy:input.requestedBy??null,metadata:JSON.stringify({})}).returning();return rowToPayoutRequest(rows[0])};if(typeof db.transaction==="function")return db.transaction(async(tx)=>{return await tx.execute(sql2`SELECT pg_advisory_xact_lock(hashtext(${lockKey}))`),insertRow(tx,tx)});return insertRow(db)}async listPayoutRequests(filter){let db=this.getDb();if(!db)return[];let table=this.table("payoutRequests"),where=filter.recipientOwnerId?eq10(col4(table,"recipientOwnerId"),filter.recipientOwnerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToPayoutRequest(r));return filter.status?mapped.filter((p)=>p.status===filter.status):mapped}async getPayoutRequest(id){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests"),rows=await db.select().from(table).where(eq10(col4(table,"id"),id)).limit(1);return rows[0]?rowToPayoutRequest(rows[0]):null}async rejectPayout(id,deciderId){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests");return await db.update(table).set({status:"rejected",decidedBy:deciderId??null,decidedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),this.getPayoutRequest(id)}async reconcilePayoutByProvider(providerId,outcome,reason){let db=this.getDb();if(!db)return null;let table=this.table("payoutRequests"),row=(await db.select().from(table).where(or(eq10(col4(table,"providerPayoutId"),providerId),eq10(col4(table,"providerTransferId"),providerId))).limit(1))[0];if(!row)return null;let previousStatus=String(row.status),nextStatus=outcome==="completed"?"completed":"failed";if(await db.update(table).set({status:nextStatus,failureReason:outcome==="failed"?reason??"Provider reported failure":null,completedAt:outcome==="completed"?new Date:row.completedAt??null,updatedAt:new Date}).where(eq10(col4(table,"id"),String(row.id))),outcome==="failed"&&previousStatus==="completed")await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:Number(row.amount),currency:String(row.currency),entryType:"adjustment",ownerType:String(row.recipientOwnerType??"seller"),ownerId:String(row.recipientOwnerId??""),referenceType:"payout_request",referenceId:String(row.id),description:`Payout reversal (${reason??"provider failure"})`});return this.getPayoutRequest(String(row.id))}async approvePayout(id,opts){let db=this.getDb();if(!db)return null;let request=await this.getPayoutRequest(id);if(!request)return null;if(request.status!=="pending")throw Error(`Payout is not pending (status: ${request.status})`);if(opts?.deciderId&&request.requestedBy&&opts.deciderId===request.requestedBy)throw Error("Approver cannot be the requester");let table=this.table("payoutRequests"),destination=opts?.destination??request.destination,claimed=await db.update(table).set({status:"processing",decidedBy:opts?.deciderId??null,decidedAt:new Date,updatedAt:new Date}).where(and5(eq10(col4(table,"id"),id),eq10(col4(table,"status"),"pending"))).returning();if(!claimed||claimed.length===0)throw Error("Payout is no longer pending (already being processed)");if(!this.provider)return this.logger.warn("No payment provider configured; payout left in processing",{id}),this.getPayoutRequest(id);let providerId,providerPayoutId;try{let result=destination?await this.provider.createTransfer({amount:request.amount,currency:request.currency,destination,transferGroup:`payout:${id}`,idempotencyKey:`payout:${id}`}):await this.provider.createPayout({amount:request.amount,currency:request.currency,idempotencyKey:`payout:${id}`});if(providerId="providerTransferId"in result?result.providerTransferId:void 0,providerPayoutId="providerPayoutId"in result?result.providerPayoutId:void 0,!result.success)throw Error(result.errorMessage||"Provider payout failed")}catch(err){let reason=err instanceof Error?err.message:"Payout execution failed";return await db.update(table).set({status:"failed",failureReason:reason,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),this.logger.error("Payout provider execution failed",{id,reason}),this.getPayoutRequest(id)}let finalize=async(exec)=>{await exec.update(table).set({status:"completed",provider:this.provider?"configured":null,providerTransferId:providerId??null,providerPayoutId:providerPayoutId??null,completedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"debit",amount:request.amount,currency:request.currency,entryType:"payout",ownerType:request.recipientOwnerType,ownerId:request.recipientOwnerId,referenceType:"payout_request",referenceId:id},exec),await this.marketplace.recordLedgerEntry({account:"payout_completed",direction:"credit",amount:request.amount,currency:request.currency,entryType:"payout",ownerType:request.recipientOwnerType,ownerId:request.recipientOwnerId,referenceType:"payout_request",referenceId:id},exec),await this.consumeSplits(request.recipientOwnerId,request.amount,request.currency,exec)};try{if(typeof db.transaction==="function")await db.transaction(async(tx)=>finalize(tx));else await finalize(db)}catch(err){let reason=err instanceof Error?err.message:"Bookkeeping failed after payout";try{await db.update(table).set({providerTransferId:providerId??null,providerPayoutId:providerPayoutId??null,failureReason:`RECONCILE_REQUIRED: ${reason}`,updatedAt:new Date}).where(eq10(col4(table,"id"),id))}catch{}this.logger.error("Payout bookkeeping failed AFTER funds moved \u2014 reconciliation required",{id,providerId,providerPayoutId,reason})}return this.getPayoutRequest(id)}async consumeSplits(ownerId,amount,currency,executor){let db=executor??this.getDb();if(!db)return;let table=this.table("splits"),splits=await this.marketplace.listSplits({recipientOwnerId:ownerId},executor),remaining=amount,now=Date.now();for(let s of splits){if(remaining<=0)break;if(s.currency!==currency)continue;if(s.status!=="pending"&&s.status!=="available")continue;if(!(s.availableForPayoutAt?new Date(s.availableForPayoutAt).getTime()<=now:!0))continue;if(s.netPayableAmount>remaining)continue;await db.update(table).set({status:"paid",updatedAt:new Date}).where(eq10(col4(table,"id"),s.id)),remaining-=s.netPayableAmount}}async openDispute(input){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),rows=await db.insert(table).values({transactionId:input.transactionId??null,provider:input.provider,providerDisputeId:input.providerDisputeId??null,ownerType:input.ownerType??null,ownerId:input.ownerId??null,amount:input.amount??null,currency:input.currency??null,reason:input.reason??null,status:"open",evidenceDueAt:input.evidenceDueAt??null,metadata:JSON.stringify(input.metadata??{})}).returning(),dispute=rowToDispute(rows[0]);if(input.amount&&input.amount>0&&input.currency)await this.marketplace.recordLedgerEntry({account:"dispute_liability",direction:"credit",amount:input.amount,currency:input.currency,entryType:"dispute",ownerType:input.ownerType,ownerId:input.ownerId,referenceType:"dispute",referenceId:dispute.id});return dispute}async listDisputes(filter){let db=this.getDb();if(!db)return[];let table=this.table("disputes"),where=filter.ownerId?eq10(col4(table,"ownerId"),filter.ownerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToDispute(r));return filter.status?mapped.filter((d)=>d.status===filter.status):mapped}async getDispute(id){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),rows=await db.select().from(table).where(eq10(col4(table,"id"),id)).limit(1);return rows[0]?rowToDispute(rows[0]):null}async resolveDispute(id,outcome){let db=this.getDb();if(!db)return null;let dispute=await this.getDispute(id);if(!dispute)return null;let table=this.table("disputes");if(await db.update(table).set({status:outcome,resolvedAt:new Date,updatedAt:new Date}).where(eq10(col4(table,"id"),id)),(outcome==="lost"||outcome==="accepted")&&dispute.amount&&dispute.currency)await this.marketplace.recordLedgerEntry({account:"seller_payable",direction:"debit",amount:dispute.amount,currency:dispute.currency,entryType:"dispute",ownerType:dispute.ownerType??void 0,ownerId:dispute.ownerId??void 0,referenceType:"dispute",referenceId:id});return this.getDispute(id)}async reconcileDisputeByProvider(providerDisputeId,status){let db=this.getDb();if(!db)return null;let table=this.table("disputes"),row=(await db.select().from(table).where(eq10(col4(table,"providerDisputeId"),providerDisputeId)).limit(1))[0];if(!row)return null;let dispute=rowToDispute(row);if(dispute.status!=="open")return dispute;let outcome=status==="won"||status==="warning_closed"?"won":status==="lost"?"lost":null;if(!outcome)return dispute;return this.resolveDispute(dispute.id,outcome)}}var col4=(table,name)=>table[name];var init_MarketplacePayoutService=()=>{};import{and as and6,eq as eq11}from"drizzle-orm";class MarketplaceService{getDb;config;logger;t;constructor(cfg){this.getDb=cfg.getDb,this.config=cfg.config,this.logger=cfg.logger.scoped("payment.marketplace"),this.t=cfg.tables}table(name){return this.t[name]}async recordLedgerEntry(entry,executor){let db=executor??this.getDb();if(!db)return;await db.insert(this.table("ledgerEntries")).values({account:entry.account,ownerType:entry.ownerType??null,ownerId:entry.ownerId??null,direction:entry.direction,amount:entry.amount,currency:entry.currency,entryType:entry.entryType,referenceType:entry.referenceType??null,referenceId:entry.referenceId??null,splitId:entry.splitId??null,description:entry.description??null,metadata:JSON.stringify({})})}async getSettlementPolicy(ownerType,ownerId){let db=this.getDb();if(!db)return null;let table=this.table("settlementPolicies"),rows=await db.select().from(table).where(and6(eq11(col5(table,"ownerType"),ownerType),eq11(col5(table,"ownerId"),ownerId))).limit(1);return rows[0]?rowToSettlementPolicy(rows[0]):null}async upsertSettlementPolicy(input){let db=this.getDb();if(!db)return null;let table=this.table("settlementPolicies"),existing=await this.getSettlementPolicy(input.ownerType,input.ownerId),values={ownerType:input.ownerType,ownerId:input.ownerId,defaultDelayDays:input.defaultDelayDays??this.config.defaultSettlementDelayDays,newSellerDelayDays:input.newSellerDelayDays??this.config.newSellerDelayDays,reserveRate:String(input.reserveRate??this.config.reserveRate),reserveDays:input.reserveDays??this.config.reserveReleaseDays,minimumPayoutAmount:input.minimumPayoutAmount??this.config.minimumPayoutAmount,currency:input.currency??null,earlyPayoutEnabled:input.earlyPayoutEnabled??!1,updatedAt:new Date};if(existing)await db.update(table).set(values).where(eq11(col5(table,"id"),existing.id));else await db.insert(table).values(values);return this.getSettlementPolicy(input.ownerType,input.ownerId)}async recordSplit(input){let db=this.getDb();if(!db)return null;let recipientType=input.recipientOwnerType??"seller",currency=input.currency??this.config.defaultCurrency,now=new Date,policy=await this.getSettlementPolicy(recipientType,input.recipientOwnerId),breakdown=computeSplitBreakdown(input,this.config,policy,now),splitTable=this.table("splits"),rows=await db.insert(splitTable).values({transactionId:input.transactionId??null,recipientOwnerType:recipientType,recipientOwnerId:input.recipientOwnerId,provider:input.provider,grossAmount:breakdown.grossAmount,providerFeeAmount:breakdown.providerFeeAmount,platformFeeAmount:breakdown.platformFeeAmount,reserveAmount:breakdown.reserveAmount,netPayableAmount:breakdown.netPayableAmount,currency,availableForPayoutAt:breakdown.availableForPayoutAt,status:"pending",sourceType:input.sourceType??null,sourceId:input.sourceId??null,metadata:JSON.stringify(input.metadata??{})}).returning(),split=rowToSplit(rows[0]);if(await this.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:breakdown.netPayableAmount,currency,entryType:"split",ownerType:recipientType,ownerId:input.recipientOwnerId,referenceType:"split",referenceId:split.id,splitId:split.id}),breakdown.platformFeeAmount>0)await this.recordLedgerEntry({account:"platform_revenue",direction:"credit",amount:breakdown.platformFeeAmount,currency,entryType:"split",referenceType:"split",referenceId:split.id,splitId:split.id});if(breakdown.providerFeeAmount>0)await this.recordLedgerEntry({account:"provider_fee",direction:"credit",amount:breakdown.providerFeeAmount,currency,entryType:"split",referenceType:"split",referenceId:split.id,splitId:split.id});if(breakdown.reserveAmount>0){let reserveDays=policy?.reserveDays??this.config.reserveReleaseDays;await db.insert(this.table("reserves")).values({ownerType:recipientType,ownerId:input.recipientOwnerId,splitId:split.id,amount:breakdown.reserveAmount,currency,reason:input.isNewSeller?"new_seller":"refund_window",status:"held",releaseAt:reserveDays>0?addDays(now,reserveDays):null,metadata:JSON.stringify({})}),await this.recordLedgerEntry({account:"reserve",direction:"credit",amount:breakdown.reserveAmount,currency,entryType:"reserve_hold",ownerType:recipientType,ownerId:input.recipientOwnerId,referenceType:"split",referenceId:split.id,splitId:split.id})}return this.logger.info("Recorded split",{splitId:split.id,gross:breakdown.grossAmount,netPayable:breakdown.netPayableAmount,reserve:breakdown.reserveAmount}),split}async listSplits(filter,executor){let db=executor??this.getDb();if(!db)return[];let table=this.table("splits"),where=filter.recipientOwnerId?eq11(col5(table,"recipientOwnerId"),filter.recipientOwnerId):void 0,mapped=(where?await db.select().from(table).where(where):await db.select().from(table)).map((r)=>rowToSplit(r));return filter.status?mapped.filter((s)=>s.status===filter.status):mapped}async releaseReserve(id){let db=this.getDb();if(!db)return!1;let table=this.table("reserves"),row=(await db.select().from(table).where(eq11(col5(table,"id"),id)).limit(1))[0];if(!row||String(row.status)!=="held")return!1;await db.update(table).set({status:"released",releasedAt:new Date,updatedAt:new Date}).where(eq11(col5(table,"id"),id));let reserveOwnerType=String(row.ownerType??row.owner_type??""),reserveOwnerId=String(row.ownerId??row.owner_id??"");return await this.recordLedgerEntry({account:"reserve",direction:"debit",amount:Number(row.amount),currency:String(row.currency),entryType:"reserve_release",ownerType:reserveOwnerType,ownerId:reserveOwnerId,referenceType:"reserve",referenceId:id}),await this.recordLedgerEntry({account:"seller_payable",direction:"credit",amount:Number(row.amount),currency:String(row.currency),entryType:"reserve_release",ownerType:reserveOwnerType,ownerId:reserveOwnerId,referenceType:"reserve",referenceId:id}),!0}async autoReleaseDueReserves(ownerType,ownerId){if(!this.config.autoReleaseReserves)return;let db=this.getDb();if(!db)return;let table=this.table("reserves"),rows=await db.select().from(table).where(and6(eq11(col5(table,"ownerType"),ownerType),eq11(col5(table,"ownerId"),ownerId))),now=Date.now();for(let r of rows){let releaseAt=r.releaseAt??r.release_at;if(String(r.status)==="held"&&releaseAt&&new Date(String(releaseAt)).getTime()<=now)await this.releaseReserve(String(r.id))}}async getBalance(ownerType,ownerId,currency,executor){let cur=currency??this.config.defaultCurrency,db=executor??this.getDb(),base={ownerType,ownerId,currency:cur,available:0,pending:0,reserved:0,payoutInFlight:0};if(!db)return base;if(!executor)await this.autoReleaseDueReserves(ownerType,ownerId);let splits=await this.listSplits({recipientOwnerId:ownerId},executor),now=Date.now();for(let s of splits){if(s.currency!==cur)continue;if(s.status!=="pending"&&s.status!=="available")continue;if(!(s.availableForPayoutAt?new Date(s.availableForPayoutAt).getTime()<=now:!0))base.pending+=s.netPayableAmount}let ledgerTable=this.table("ledgerEntries"),ledgerRows=await db.select().from(ledgerTable).where(and6(eq11(col5(ledgerTable,"ownerType"),ownerType),eq11(col5(ledgerTable,"ownerId"),ownerId))),payableNet=0;for(let e of ledgerRows){if(String(e.account)!=="seller_payable"||String(e.currency)!==cur)continue;let amt=Number(e.amount);payableNet+=String(e.direction)==="credit"?amt:-amt}base.available=payableNet-base.pending;let reserveTable=this.table("reserves"),reserveRows=await db.select().from(reserveTable).where(and6(eq11(col5(reserveTable,"ownerType"),ownerType),eq11(col5(reserveTable,"ownerId"),ownerId)));for(let r of reserveRows)if(String(r.status)==="held"&&String(r.currency)===cur)base.reserved+=Number(r.amount);let payoutTable=this.table("payoutRequests"),payoutRows=await db.select().from(payoutTable).where(and6(eq11(col5(payoutTable,"recipientOwnerType"),ownerType),eq11(col5(payoutTable,"recipientOwnerId"),ownerId)));for(let p of payoutRows){let st=String(p.status);if((st==="pending"||st==="approved"||st==="processing")&&String(p.currency)===cur)base.payoutInFlight+=Number(p.amount),base.available-=Number(p.amount)}if(base.available<0)base.available=0;return base}}var col5=(table,name)=>table[name];var init_MarketplaceService=()=>{};var pick=(schemaTables,camel,snake)=>schemaTables[camel]??schemaTables[snake]??{},createMarketplaceServices=(params)=>{let config=resolveMarketplaceConfig(params.options.payment);if(!config.enabled)return null;let tables={splits:pick(params.schemaTables,"paymentSplits","payment_splits"),ledgerEntries:pick(params.schemaTables,"paymentLedgerEntries","payment_ledger_entries"),settlementPolicies:pick(params.schemaTables,"paymentSettlementPolicies","payment_settlement_policies"),reserves:pick(params.schemaTables,"paymentReserves","payment_reserves"),payoutRequests:pick(params.schemaTables,"paymentPayoutRequests","payment_payout_requests"),disputes:pick(params.schemaTables,"paymentDisputes","payment_disputes")},serviceConfig={getDb:params.getDb,provider:params.provider,config,logger:params.logger,tables},marketplaceService=new MarketplaceService(serviceConfig),payoutService=new MarketplacePayoutService(serviceConfig,marketplaceService);return{marketplaceService,payoutService}};var init_Marketplace=__esm(()=>{init_MarketplacePayoutService();init_MarketplaceService();init_MarketplacePayoutService();init_MarketplaceService()});var loadStripe=(config)=>{try{let StripeModule=__require("stripe");return new(StripeModule.default??StripeModule)(config.secretKey,{maxNetworkRetries:config.maxNetworkRetries??2,timeout:config.timeout??30000,...config.apiVersion?{apiVersion:config.apiVersion}:{}})}catch(error){return logger.scoped("payment.init").error("Failed to load stripe package. Install it: bun add stripe",error),null}},createStripeProvider=(config)=>{let stripe=loadStripe(config);if(!stripe)return null;let providerName="stripe";return{get name(){return providerName},async initialize3DS(request){try{let params={amount:Math.round(Number(request.paidPrice)*100),currency:request.currency.toLowerCase(),payment_method_types:["card"],description:`Order ${request.conversationId}`,metadata:{conversationId:request.conversationId,basketId:request.basketId??"",buyerEmail:request.buyer.email,buyerId:request.buyer.id}};if("cardToken"in request.paymentCard&&request.paymentCard.cardToken)params.payment_method=request.paymentCard.cardToken,params.confirm=!0,params.return_url=request.callbackUrl;let paymentIntent=await stripe.paymentIntents.create(params),clientSecret=paymentIntent.client_secret,piId=paymentIntent.id,status=paymentIntent.status;return{success:!0,paymentId:piId,threeDSHtmlContent:clientSecret,signature:status,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment initialization failed",rawResponse:{error:err.message}}}},async complete3DS(request){try{let paymentIntent=await stripe.paymentIntents.retrieve(request.paymentId,{expand:["payment_method"]}),status=paymentIntent.status,isSuccess=status==="succeeded",amountReceived=paymentIntent.amount_received??0,card=paymentIntent.payment_method?.card;return{success:isSuccess,paymentId:paymentIntent.id,price:amountReceived/100,paidPrice:amountReceived/100,currency:paymentIntent.currency?.toUpperCase(),cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,cardFamily:card?.funding??void 0,lastFourDigits:card?.last4??void 0,authCode:status,errorCode:isSuccess?void 0:"PAYMENT_NOT_SUCCEEDED",errorMessage:isSuccess?void 0:`Payment status: ${status}`,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment completion failed",rawResponse:{error:err.message}}}},async getPaymentDetail(request){try{let paymentIntent=await stripe.paymentIntents.retrieve(request.paymentId,{expand:["payment_method"]}),amountReceived=paymentIntent.amount_received??paymentIntent.amount??0,card=paymentIntent.payment_method?.card;return{success:!0,paymentId:paymentIntent.id,price:amountReceived/100,paidPrice:amountReceived/100,currency:paymentIntent.currency?.toUpperCase(),cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,lastFourDigits:card?.last4??void 0,binNumber:void 0,rawResponse:paymentIntent}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Payment detail query failed",rawResponse:{error:err.message}}}},async queryBin(_request){return{success:!1,errorCode:"NOT_SUPPORTED",errorMessage:"BIN query is not supported by Stripe. Card info is available after payment method creation.",rawResponse:{}}},async saveCard(request){try{let customer=await stripe.customers.create({email:request.email,metadata:{externalId:request.externalId??""}}),paymentMethod=await stripe.paymentMethods.create({type:"card",card:{number:request.card.cardNumber,exp_month:Number(request.card.expireMonth),exp_year:Number(request.card.expireYear)}});await stripe.paymentMethods.attach(paymentMethod.id,{customer:customer.id});let card=paymentMethod.card;return{success:!0,cardUserKey:customer.id,cardToken:paymentMethod.id,cardAlias:request.card.cardAlias,binNumber:void 0,lastFourDigits:card?.last4??void 0,cardType:card?.brand??void 0,cardAssociation:card?.brand??void 0,cardFamily:card?.funding??void 0,cardBankName:void 0,rawResponse:{customer,paymentMethod}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to save card",rawResponse:{error:err.message}}}},async listCards(request){try{let response=await stripe.paymentMethods.list({customer:request.cardUserKey,type:"card"}),cards=response.data.map((pm)=>{let card=pm.card;return{cardToken:pm.id,cardAlias:pm.metadata?.alias??"",binNumber:"",lastFourDigits:card?.last4??"",cardType:card?.brand??"",cardAssociation:card?.brand??"",cardFamily:card?.funding??"",cardBankName:"",cardBankCode:0}});return{success:!0,cardUserKey:request.cardUserKey,cards,rawResponse:response}}catch(error){let err=error;return{success:!1,cards:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list cards",rawResponse:{error:err.message}}}},async deleteCard(request){try{return await stripe.paymentMethods.detach(request.cardToken),{success:!0,rawResponse:{detached:request.cardToken}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to delete card",rawResponse:{error:err.message}}}},async refund(request){try{let amountInCents=Math.round(Number(request.price)*100),refund=await stripe.refunds.create({payment_intent:request.paymentTransactionId,amount:amountInCents});return{success:refund.status==="succeeded",paymentId:refund.payment_intent,price:amountInCents/100,currency:refund.currency?.toUpperCase(),errorCode:refund.status!=="succeeded"?"REFUND_FAILED":void 0,errorMessage:refund.status!=="succeeded"?`Refund status: ${refund.status}`:void 0,rawResponse:refund}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Refund failed",rawResponse:{error:err.message}}}},parseCallback(formData){let paymentIntent=formData.payment_intent??formData.paymentId??"",redirectStatus=formData.redirect_status??"";return{success:redirectStatus==="succeeded",paymentId:paymentIntent,conversationId:formData.conversationId??"",mdStatus:redirectStatus,rawData:formData}},async registerSubMerchant(request){try{let account=await stripe.accounts.create({type:"express",country:request.currency==="TRY"?"TR":void 0,email:request.email,business_type:request.subMerchantType==="personal"?"individual":"company",metadata:{externalId:request.subMerchantExternalId,name:request.name,contactName:request.contactName??"",contactSurname:request.contactSurname??""},capabilities:{transfers:{requested:!0}}});return{success:!0,subMerchantKey:account.id,rawResponse:account}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create connected account",rawResponse:{error:err.message}}}},async updateSubMerchant(request){try{let params={metadata:{}},meta=params.metadata;if(request.name)meta.name=request.name;if(request.email)params.email=request.email;if(request.contactName)meta.contactName=request.contactName;if(request.contactSurname)meta.contactSurname=request.contactSurname;return await stripe.accounts.update(request.subMerchantKey,params),{success:!0,rawResponse:{updated:request.subMerchantKey}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update connected account",rawResponse:{error:err.message}}}},async getSubMerchant(request){try{let account=await stripe.accounts.retrieve(request.subMerchantExternalId),meta=account.metadata??{};return{success:!0,subMerchantKey:account.id,name:meta.name,email:account.email,subMerchantType:account.business_type==="individual"?"personal":"company",currency:account.default_currency?.toUpperCase(),rawResponse:account}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to retrieve connected account",rawResponse:{error:err.message}}}},async approveSplit(request){return{success:!0,paymentTransactionId:request.paymentTransactionId,rawResponse:{message:"Stripe transfers are automatic; no manual approval needed."}}},async disapproveSplit(request){try{let reversal=await stripe.transfers.createReversal(request.paymentTransactionId);return{success:!0,paymentTransactionId:reversal.id,rawResponse:reversal}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to reverse transfer",rawResponse:{error:err.message}}}},async createProduct(request){try{let params={name:request.name};if(request.description)params.description=request.description;if(request.type)params.type=request.type;if(request.images)params.images=request.images;if(request.unitLabel)params.unit_label=request.unitLabel;if(request.url)params.url=request.url;if(request.metadata)params.metadata=request.metadata;let product=await stripe.products.create(params);return{success:!0,providerProductId:product.id,name:product.name,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create product",rawResponse:{error:err.message}}}},async updateProduct(request){try{let params={};if(request.name)params.name=request.name;if(request.description!==void 0)params.description=request.description;if(request.images)params.images=request.images;if(request.unitLabel!==void 0)params.unit_label=request.unitLabel;if(request.url!==void 0)params.url=request.url;if(request.active!==void 0)params.active=request.active;if(request.metadata)params.metadata=request.metadata;let product=await stripe.products.update(request.providerProductId,params);return{success:!0,providerProductId:product.id,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update product",rawResponse:{error:err.message}}}},async getProduct(request){try{let product=await stripe.products.retrieve(request.providerProductId);return{success:!0,providerProductId:product.id,name:product.name,description:product.description,type:product.type,active:product.active,images:product.images,unitLabel:product.unit_label,url:product.url,metadata:product.metadata,rawResponse:product}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get product",rawResponse:{error:err.message}}}},async listProducts(request){try{let params={};if(request.active!==void 0)params.active=request.active;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.products.list(params);return{success:!0,products:response.data.map((p)=>({providerProductId:p.id,name:p.name,description:p.description,type:p.type,active:p.active,images:p.images,unitLabel:p.unit_label,metadata:p.metadata})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,products:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list products",rawResponse:{error:err.message}}}},async archiveProduct(request){try{return await stripe.products.update(request.providerProductId,{active:!1}),{success:!0,rawResponse:{archived:request.providerProductId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to archive product",rawResponse:{error:err.message}}}},async createPrice(request){try{let params={product:request.providerProductId,currency:request.currency.toLowerCase(),unit_amount:request.unitAmount};if(request.nickname)params.nickname=request.nickname;if(request.billingScheme)params.billing_scheme=request.billingScheme;if(request.unitAmountDecimal)params.unit_amount_decimal=request.unitAmountDecimal;if(request.metadata)params.metadata=request.metadata;if(request.recurring)params.recurring={interval:request.recurring.interval,...request.recurring.intervalCount?{interval_count:request.recurring.intervalCount}:{},...request.recurring.usageType?{usage_type:request.recurring.usageType}:{},...request.recurring.aggregateUsage?{aggregate_usage:request.recurring.aggregateUsage}:{}};if(request.transformQuantity)params.transform_quantity={divide_by:request.transformQuantity.divideBy,round:request.transformQuantity.round};let price=await stripe.prices.create(params);return{success:!0,providerPriceId:price.id,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create price",rawResponse:{error:err.message}}}},async updatePrice(request){try{let params={};if(request.nickname!==void 0)params.nickname=request.nickname;if(request.active!==void 0)params.active=request.active;if(request.metadata)params.metadata=request.metadata;let price=await stripe.prices.update(request.providerPriceId,params);return{success:!0,providerPriceId:price.id,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update price",rawResponse:{error:err.message}}}},async getPrice(request){try{let price=await stripe.prices.retrieve(request.providerPriceId),rec=price.recurring;return{success:!0,providerPriceId:price.id,providerProductId:price.product,currency:price.currency?.toUpperCase(),unitAmount:price.unit_amount,type:price.type,billingScheme:price.billing_scheme,nickname:price.nickname,active:price.active,recurring:rec?{interval:rec.interval,intervalCount:rec.interval_count??1,usageType:rec.usage_type}:void 0,metadata:price.metadata,rawResponse:price}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get price",rawResponse:{error:err.message}}}},async listPrices(request){try{let params={};if(request.providerProductId)params.product=request.providerProductId;if(request.active!==void 0)params.active=request.active;if(request.type)params.type=request.type;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.prices.list(params);return{success:!0,prices:response.data.map((p)=>{let rec=p.recurring;return{providerPriceId:p.id,providerProductId:p.product,currency:p.currency?.toUpperCase(),unitAmount:p.unit_amount,type:p.type,billingScheme:p.billing_scheme,nickname:p.nickname,active:p.active,recurring:rec?{interval:rec.interval,intervalCount:rec.interval_count??1,usageType:rec.usage_type}:void 0,metadata:p.metadata}}),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,prices:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list prices",rawResponse:{error:err.message}}}},async archivePrice(request){try{return await stripe.prices.update(request.providerPriceId,{active:!1}),{success:!0,rawResponse:{archived:request.providerPriceId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to archive price",rawResponse:{error:err.message}}}},async createCustomer(request){try{let params={email:request.email};if(request.name)params.name=request.name;if(request.phone)params.phone=request.phone;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.address)params.address={line1:request.address.line1,line2:request.address.line2,city:request.address.city,state:request.address.state,postal_code:request.address.postalCode,country:request.address.country};let customer=await stripe.customers.create(params);if(request.taxId)try{let createTaxId=stripe.taxIds?.create;if(createTaxId)await createTaxId({customer:customer.id,type:request.taxId.type,value:request.taxId.value})}catch(_){}return{success:!0,providerCustomerId:customer.id,email:customer.email,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create customer",rawResponse:{error:err.message}}}},async updateCustomer(request){try{let params={};if(request.email)params.email=request.email;if(request.name!==void 0)params.name=request.name;if(request.phone!==void 0)params.phone=request.phone;if(request.description!==void 0)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.address)params.address={line1:request.address.line1,line2:request.address.line2,city:request.address.city,state:request.address.state,postal_code:request.address.postalCode,country:request.address.country};let customer=await stripe.customers.update(request.providerCustomerId,params);return{success:!0,providerCustomerId:customer.id,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update customer",rawResponse:{error:err.message}}}},async getCustomer(request){try{let customer=await stripe.customers.retrieve(request.providerCustomerId);return{success:!0,providerCustomerId:customer.id,email:customer.email,name:customer.name,phone:customer.phone,description:customer.description,metadata:customer.metadata,rawResponse:customer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get customer",rawResponse:{error:err.message}}}},async listCustomers(request){try{let params={};if(request.email)params.email=request.email;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.customers.list(params);return{success:!0,customers:response.data.map((c)=>({providerCustomerId:c.id,email:c.email,name:c.name,phone:c.phone})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,customers:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list customers",rawResponse:{error:err.message}}}},async deleteCustomer(request){try{return await stripe.customers.del(request.providerCustomerId),{success:!0,rawResponse:{deleted:request.providerCustomerId}}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to delete customer",rawResponse:{error:err.message}}}},async createSubscription(request){try{let params={customer:request.providerCustomerId,items:request.items.map((i)=>({price:i.providerPriceId,...i.quantity!==void 0?{quantity:i.quantity}:{}}))};if(request.trialPeriodDays)params.trial_period_days=request.trialPeriodDays;if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.cancelAtPeriodEnd!==void 0)params.cancel_at_period_end=request.cancelAtPeriodEnd;if(request.metadata)params.metadata=request.metadata;if(request.defaultPaymentMethod)params.default_payment_method=request.defaultPaymentMethod;params.payment_behavior="default_incomplete",params.payment_settings={save_default_payment_method:"on_subscription"},params.expand=["latest_invoice.payment_intent"];let sub=await stripe.subscriptions.create(params),pi=sub.latest_invoice?.payment_intent;return{success:!0,providerSubscriptionId:sub.id,status:sub.status,currentPeriodStart:sub.current_period_start?new Date(sub.current_period_start*1000).toISOString():void 0,currentPeriodEnd:sub.current_period_end?new Date(sub.current_period_end*1000).toISOString():void 0,clientSecret:pi?.client_secret,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create subscription",rawResponse:{error:err.message}}}},async updateSubscription(request){try{let params={};if(request.items)params.items=request.items.map((i)=>({...i.id?{id:i.id}:{},price:i.providerPriceId,...i.quantity!==void 0?{quantity:i.quantity}:{}}));if(request.cancelAtPeriodEnd!==void 0)params.cancel_at_period_end=request.cancelAtPeriodEnd;if(request.trialEnd!==void 0)params.trial_end=request.trialEnd;if(request.metadata)params.metadata=request.metadata;if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.defaultPaymentMethod)params.default_payment_method=request.defaultPaymentMethod;if(request.prorationBehavior)params.proration_behavior=request.prorationBehavior;let sub=await stripe.subscriptions.update(request.providerSubscriptionId,params);return{success:!0,providerSubscriptionId:sub.id,status:sub.status,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to update subscription",rawResponse:{error:err.message}}}},async cancelSubscription(request){try{if(request.cancelAtPeriodEnd){let sub2=await stripe.subscriptions.update(request.providerSubscriptionId,{cancel_at_period_end:!0});return{success:!0,providerSubscriptionId:sub2.id,status:sub2.status,rawResponse:sub2}}let params={};if(request.invoiceNow)params.invoice_now=request.invoiceNow;if(request.prorate)params.prorate=request.prorate;let sub=await stripe.subscriptions.cancel(request.providerSubscriptionId,params);return{success:!0,providerSubscriptionId:sub.id,status:sub.status,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to cancel subscription",rawResponse:{error:err.message}}}},async getSubscription(request){try{let sub=await stripe.subscriptions.retrieve(request.providerSubscriptionId),itemsData=sub.items?.data??[];return{success:!0,providerSubscriptionId:sub.id,providerCustomerId:sub.customer,status:sub.status,currentPeriodStart:sub.current_period_start?new Date(sub.current_period_start*1000).toISOString():void 0,currentPeriodEnd:sub.current_period_end?new Date(sub.current_period_end*1000).toISOString():void 0,cancelAtPeriodEnd:sub.cancel_at_period_end,canceledAt:sub.canceled_at?new Date(sub.canceled_at*1000).toISOString():void 0,trialStart:sub.trial_start?new Date(sub.trial_start*1000).toISOString():void 0,trialEnd:sub.trial_end?new Date(sub.trial_end*1000).toISOString():void 0,items:itemsData.map((i)=>({id:i.id,providerPriceId:i.price?.id??i.price,quantity:i.quantity??1})),metadata:sub.metadata,rawResponse:sub}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get subscription",rawResponse:{error:err.message}}}},async listSubscriptions(request){try{let params={};if(request.providerCustomerId)params.customer=request.providerCustomerId;if(request.status)params.status=request.status;if(request.providerPriceId)params.price=request.providerPriceId;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.subscriptions.list(params);return{success:!0,subscriptions:response.data.map((s)=>({providerSubscriptionId:s.id,providerCustomerId:s.customer,status:s.status,currentPeriodStart:s.current_period_start?new Date(s.current_period_start*1000).toISOString():void 0,currentPeriodEnd:s.current_period_end?new Date(s.current_period_end*1000).toISOString():void 0,cancelAtPeriodEnd:s.cancel_at_period_end})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,subscriptions:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list subscriptions",rawResponse:{error:err.message}}}},async reportUsage(request){try{let params={quantity:request.quantity,action:request.action??"increment"};if(request.timestamp)params.timestamp=request.timestamp;let record=await stripe.subscriptionItems.usageRecords.create(request.subscriptionItemId,params);return{success:!0,usageRecordId:record.id,quantity:record.quantity,timestamp:record.timestamp,rawResponse:record}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to report usage",rawResponse:{error:err.message}}}},async listUsageRecordSummaries(request){try{let params={};if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.subscriptionItems.usageRecordSummaries.list(request.subscriptionItemId,params);return{success:!0,summaries:response.data.map((s)=>({id:s.id,totalUsage:s.total_usage,period:s.period,subscriptionItem:s.subscription_item})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,summaries:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list usage summaries",rawResponse:{error:err.message}}}},async createInvoice(request){try{let params={customer:request.providerCustomerId};if(request.collectionMethod)params.collection_method=request.collectionMethod;if(request.daysUntilDue)params.days_until_due=request.daysUntilDue;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.autoAdvance!==void 0)params.auto_advance=request.autoAdvance;let invoice=await stripe.invoices.create(params);if(request.items?.length)for(let item of request.items){let lineParams={customer:request.providerCustomerId,invoice:invoice.id};if(item.providerPriceId)lineParams.price=item.providerPriceId;if(item.quantity)lineParams.quantity=item.quantity;if(item.unitAmount!==void 0)lineParams.unit_amount=item.unitAmount;if(item.currency)lineParams.currency=item.currency;if(item.description)lineParams.description=item.description;await stripe.invoiceItems.create(lineParams)}return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create invoice",rawResponse:{error:err.message}}}},async finalizeInvoice(request){try{let params={};if(request.autoAdvance!==void 0)params.auto_advance=request.autoAdvance;let invoice=await stripe.invoices.finalizeInvoice(request.providerInvoiceId,params);return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to finalize invoice",rawResponse:{error:err.message}}}},async sendInvoice(request){try{let invoice=await stripe.invoices.sendInvoice(request.providerInvoiceId);return{success:!0,providerInvoiceId:invoice.id,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to send invoice",rawResponse:{error:err.message}}}},async voidInvoice(request){try{let invoice=await stripe.invoices.voidInvoice(request.providerInvoiceId);return{success:!0,providerInvoiceId:invoice.id,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to void invoice",rawResponse:{error:err.message}}}},async payInvoice(request){try{let params={};if(request.paymentMethod)params.payment_method=request.paymentMethod;let invoice=await stripe.invoices.pay(request.providerInvoiceId,params);return{success:!0,providerInvoiceId:invoice.id,status:invoice.status,amountPaid:invoice.amount_paid,amountDue:invoice.amount_due,rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to pay invoice",rawResponse:{error:err.message}}}},async getInvoice(request){try{let invoice=await stripe.invoices.retrieve(request.providerInvoiceId),linesData=invoice.lines?.data??[];return{success:!0,providerInvoiceId:invoice.id,providerCustomerId:invoice.customer,providerSubscriptionId:invoice.subscription,status:invoice.status,amountDue:invoice.amount_due,amountPaid:invoice.amount_paid,amountRemaining:invoice.amount_remaining,currency:invoice.currency?.toUpperCase(),description:invoice.description,hostedInvoiceUrl:invoice.hosted_invoice_url,invoicePdf:invoice.invoice_pdf,dueDate:invoice.due_date?new Date(invoice.due_date*1000).toISOString():void 0,periodStart:invoice.period_start?new Date(invoice.period_start*1000).toISOString():void 0,periodEnd:invoice.period_end?new Date(invoice.period_end*1000).toISOString():void 0,metadata:invoice.metadata,lines:linesData.map((l)=>({id:l.id,description:l.description,amount:l.amount,currency:l.currency?.toUpperCase(),quantity:l.quantity,providerPriceId:l.price?.id})),rawResponse:invoice}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get invoice",rawResponse:{error:err.message}}}},async listInvoices(request){try{let params={};if(request.providerCustomerId)params.customer=request.providerCustomerId;if(request.providerSubscriptionId)params.subscription=request.providerSubscriptionId;if(request.status)params.status=request.status;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.invoices.list(params);return{success:!0,invoices:response.data.map((inv)=>({providerInvoiceId:inv.id,providerCustomerId:inv.customer,status:inv.status,amountDue:inv.amount_due,amountPaid:inv.amount_paid,currency:inv.currency?.toUpperCase(),dueDate:inv.due_date?new Date(inv.due_date*1000).toISOString():void 0,hostedInvoiceUrl:inv.hosted_invoice_url})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,invoices:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list invoices",rawResponse:{error:err.message}}}},async getBalance(){try{let bal=await stripe.balance.retrieve(),mapAmounts=(arr)=>(arr??[]).map((a)=>({amount:a.amount,currency:a.currency?.toUpperCase()}));return{success:!0,available:mapAmounts(bal.available),pending:mapAmounts(bal.pending),connectReserved:mapAmounts(bal.connect_reserved),rawResponse:bal}}catch(error){let err=error;return{success:!1,available:[],pending:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get balance",rawResponse:{error:err.message}}}},async createPayout(request){try{let params={amount:request.amount,currency:request.currency.toLowerCase()};if(request.destination)params.destination=request.destination;if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.method)params.method=request.method;if(request.sourceType)params.source_type=request.sourceType;if(request.statementDescriptor)params.statement_descriptor=request.statementDescriptor;let payout=await stripe.payouts.create(params,request.idempotencyKey?{idempotencyKey:request.idempotencyKey}:void 0);return{success:!0,providerPayoutId:payout.id,amount:payout.amount,currency:payout.currency?.toUpperCase(),status:payout.status,arrivalDate:payout.arrival_date?new Date(payout.arrival_date*1000).toISOString():void 0,method:payout.method,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create payout",rawResponse:{error:err.message}}}},async getPayout(request){try{let payout=await stripe.payouts.retrieve(request.providerPayoutId);return{success:!0,providerPayoutId:payout.id,amount:payout.amount,currency:payout.currency?.toUpperCase(),status:payout.status,arrivalDate:payout.arrival_date?new Date(payout.arrival_date*1000).toISOString():void 0,method:payout.method,description:payout.description,destination:payout.destination,metadata:payout.metadata,failureCode:payout.failure_code,failureMessage:payout.failure_message,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to get payout",rawResponse:{error:err.message}}}},async cancelPayout(request){try{let payout=await stripe.payouts.cancel(request.providerPayoutId);return{success:!0,providerPayoutId:payout.id,status:payout.status,rawResponse:payout}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to cancel payout",rawResponse:{error:err.message}}}},async listPayouts(request){try{let params={};if(request.status)params.status=request.status;if(request.destination)params.destination=request.destination;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;if(request.arrivalDateGte||request.arrivalDateLte){let arrival={};if(request.arrivalDateGte)arrival.gte=request.arrivalDateGte;if(request.arrivalDateLte)arrival.lte=request.arrivalDateLte;params.arrival_date=arrival}let response=await stripe.payouts.list(params);return{success:!0,payouts:response.data.map((p)=>({providerPayoutId:p.id,amount:p.amount,currency:p.currency?.toUpperCase(),status:p.status,arrivalDate:p.arrival_date?new Date(p.arrival_date*1000).toISOString():void 0,method:p.method,description:p.description})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,payouts:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list payouts",rawResponse:{error:err.message}}}},async createTransfer(request){try{let params={amount:request.amount,currency:request.currency.toLowerCase(),destination:request.destination};if(request.description)params.description=request.description;if(request.metadata)params.metadata=request.metadata;if(request.sourceTransaction)params.source_transaction=request.sourceTransaction;if(request.transferGroup)params.transfer_group=request.transferGroup;let transfer=await stripe.transfers.create(params,request.idempotencyKey?{idempotencyKey:request.idempotencyKey}:void 0);return{success:!0,providerTransferId:transfer.id,amount:transfer.amount,currency:transfer.currency?.toUpperCase(),destination:transfer.destination,rawResponse:transfer}}catch(error){let err=error;return{success:!1,errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to create transfer",rawResponse:{error:err.message}}}},async listTransfers(request){try{let params={};if(request.destination)params.destination=request.destination;if(request.transferGroup)params.transfer_group=request.transferGroup;if(request.limit)params.limit=request.limit;if(request.startingAfter)params.starting_after=request.startingAfter;let response=await stripe.transfers.list(params);return{success:!0,transfers:response.data.map((t)=>({providerTransferId:t.id,amount:t.amount,currency:t.currency?.toUpperCase(),destination:t.destination,description:t.description,created:t.created?new Date(t.created*1000).toISOString():void 0})),hasMore:response.has_more,rawResponse:response}}catch(error){let err=error;return{success:!1,transfers:[],errorCode:err.code??"STRIPE_ERROR",errorMessage:err.message??"Failed to list transfers",rawResponse:{error:err.message}}}}}},verifyStripeWebhook=async(rawBody,signature,webhookSecret)=>{try{let StripeModule=__require("stripe"),Stripe=StripeModule.default??StripeModule,stripe=new Stripe("sk_dummy_for_webhook_verification"),factory=Stripe,cryptoProvider=typeof factory.createSubtleCryptoProvider==="function"?factory.createSubtleCryptoProvider():typeof factory.createNodeCryptoProvider==="function"?factory.createNodeCryptoProvider():void 0;return{success:!0,event:await stripe.webhooks.constructEventAsync(rawBody,signature,webhookSecret,void 0,cryptoProvider)}}catch(error){return{success:!1,event:null,error:error instanceof Error?error.message:"Webhook verification failed"}}};var init_StripeProvider=__esm(()=>{init_Logger2()});var resolveEnvValue3=(value)=>{let envValue=process.env[value];if(envValue)return envValue;return value},createPaymentService=(config)=>{let paymentConfig=config.payment;if(!paymentConfig?.enabled)return null;let log2=logger.scoped("payment.init"),providerName=paymentConfig.provider;if(!providerName)return log2.warn("payment.enabled=true but no provider specified"),null;let providerConfig=null,provider=null,webhookSecret;switch(providerName){case"iyzico":{let iyzicoConf=paymentConfig.iyzico;if(!iyzicoConf)return log2.warn("payment.provider=iyzico but no iyzico config block"),null;let apiUrl=resolveEnvValue3(iyzicoConf.apiUrl),apiKey=resolveEnvValue3(iyzicoConf.apiKey),secretKey=resolveEnvValue3(iyzicoConf.secretKey);if(!apiUrl||!apiKey||!secretKey)return log2.warn("iyzico credentials missing \u2014 ensure env vars are set",{apiUrl:iyzicoConf.apiUrl,apiKeyEnv:iyzicoConf.apiKey,secretKeyEnv:iyzicoConf.secretKey}),null;providerConfig={name:"iyzico",apiUrl,apiKey,secretKey},provider=createIyzicoProvider(providerConfig);break}case"stripe":{let stripeConf=paymentConfig.stripe;if(!stripeConf)return log2.warn("payment.provider=stripe but no stripe config block"),null;let secretKey=resolveEnvValue3(stripeConf.secretKey),whSecret=resolveEnvValue3(stripeConf.webhookSecret);if(!secretKey)return log2.warn("Stripe secret key missing \u2014 ensure env var is set",{secretKeyEnv:stripeConf.secretKey}),null;if(!whSecret)log2.warn("Stripe webhook secret missing \u2014 webhook verification will fail",{webhookSecretEnv:stripeConf.webhookSecret});webhookSecret=whSecret,provider=createStripeProvider({secretKey,webhookSecret:whSecret,apiVersion:stripeConf.apiVersion,maxNetworkRetries:stripeConf.maxNetworkRetries,timeout:stripeConf.timeout});break}default:return log2.warn(`Unknown provider: ${providerName}`),null}if(!provider)return null;return log2.info(`Initialized provider: ${providerName}`),{provider:providerName,providerInstance:provider,webhookSecret}};var init_PaymentService=__esm(()=>{init_Logger2();init_IyzicoProvider();init_StripeProvider()});var exports_Payment={};__export(exports_Payment,{verifyStripeWebhook:()=>verifyStripeWebhook,resolveMarketplaceConfig:()=>resolveMarketplaceConfig,createStripeProvider:()=>createStripeProvider,createPaymentService:()=>createPaymentService,createMarketplaceServices:()=>createMarketplaceServices,createIyzicoProvider:()=>createIyzicoProvider,computeSplitBreakdown:()=>computeSplitBreakdown,MarketplaceService:()=>MarketplaceService,MarketplacePayoutService:()=>MarketplacePayoutService});var init_Payment=__esm(()=>{init_IyzicoProvider();init_Marketplace();init_PaymentService();init_StripeProvider()});import{randomUUID as randomUUID4}from"crypto";class RateLimiter{redis;logger;config;constructor(deps){this.redis=deps.redis,this.logger=deps.logger,this.config=this.mergeConfig(deps.config)}mergeConfig(config){return{enabled:config.enabled??DEFAULT_CONFIG4.enabled,strategy:config.strategy??DEFAULT_CONFIG4.strategy,keyPrefix:config.keyPrefix??DEFAULT_CONFIG4.keyPrefix,failClosed:config.failClosed,authRoutes:{window:config.authRoutes?.window??DEFAULT_CONFIG4.authRoutes.window,max:config.authRoutes?.max??DEFAULT_CONFIG4.authRoutes.max,login:{window:config.authRoutes?.login?.window??DEFAULT_AUTH_LOGIN.window,max:config.authRoutes?.login?.max??DEFAULT_AUTH_LOGIN.max,blockDuration:config.authRoutes?.login?.blockDuration??DEFAULT_AUTH_LOGIN.blockDuration},register:{window:config.authRoutes?.register?.window??DEFAULT_AUTH_REGISTER.window,max:config.authRoutes?.register?.max??DEFAULT_AUTH_REGISTER.max,blockDuration:config.authRoutes?.register?.blockDuration??DEFAULT_AUTH_REGISTER.blockDuration},passwordReset:{window:config.authRoutes?.passwordReset?.window??DEFAULT_AUTH_PASSWORD_RESET.window,max:config.authRoutes?.passwordReset?.max??DEFAULT_AUTH_PASSWORD_RESET.max,blockDuration:config.authRoutes?.passwordReset?.blockDuration??DEFAULT_AUTH_PASSWORD_RESET.blockDuration},magicLink:{window:config.authRoutes?.magicLink?.window??DEFAULT_AUTH_MAGIC_LINK.window,max:config.authRoutes?.magicLink?.max??DEFAULT_AUTH_MAGIC_LINK.max,blockDuration:config.authRoutes?.magicLink?.blockDuration??DEFAULT_AUTH_MAGIC_LINK.blockDuration}},publicRoutes:{window:config.publicRoutes?.window??DEFAULT_CONFIG4.publicRoutes.window,max:config.publicRoutes?.max??DEFAULT_CONFIG4.publicRoutes.max},privateRoutes:{window:config.privateRoutes?.window??DEFAULT_CONFIG4.privateRoutes.window,max:config.privateRoutes?.max??DEFAULT_CONFIG4.privateRoutes.max},byIp:config.byIp??DEFAULT_CONFIG4.byIp,byUserId:config.byUserId??DEFAULT_CONFIG4.byUserId,byEndpoint:config.byEndpoint??DEFAULT_CONFIG4.byEndpoint,skipSuccessfulRequests:config.skipSuccessfulRequests??DEFAULT_CONFIG4.skipSuccessfulRequests,headers:{remaining:config.headers?.remaining??DEFAULT_CONFIG4.headers.remaining,reset:config.headers?.reset??DEFAULT_CONFIG4.headers.reset,limit:config.headers?.limit??DEFAULT_CONFIG4.headers.limit},whitelist:config.whitelist??DEFAULT_CONFIG4.whitelist,blacklist:config.blacklist??DEFAULT_CONFIG4.blacklist}}parseTimeToMs(time){let match=time.match(/^(\d+)(ms|s|m|h|d)$/);if(!match||!match[1]||!match[2])return 60000;let value=parseInt(match[1],10);switch(match[2]){case"ms":return value;case"s":return value*1000;case"m":return value*60*1000;case"h":return value*60*60*1000;case"d":return value*24*60*60*1000;default:return 60000}}buildKey(params){let parts=[this.config.keyPrefix,params.category];if(params.authType&&params.authType!=="other")parts.push(params.authType);if(this.config.byIp&&params.ip)parts.push(`ip:${params.ip}`);if(this.config.byUserId&&params.userId)parts.push(`user:${params.userId}`);if(this.config.byEndpoint&&params.endpoint)parts.push(`ep:${params.endpoint.replace(/\//g,"_")}`);return parts.join(":")}getLimits(category,authType){if(category==="auth"){if(authType&&authType!=="other"){let authConfig=this.config.authRoutes[authType];if(authConfig)return authConfig}return{window:this.config.authRoutes.window,max:this.config.authRoutes.max}}if(category==="public")return this.config.publicRoutes;return this.config.privateRoutes}isWhitelisted(ip){return this.config.whitelist.some((pattern)=>{if(pattern.includes("*"))return new RegExp(`^${pattern.replace(/\*/g,".*")}$`).test(ip);return pattern===ip})}isBlacklisted(ip){return this.config.blacklist.some((pattern)=>{if(pattern.includes("*"))return new RegExp(`^${pattern.replace(/\*/g,".*")}$`).test(ip);return pattern===ip})}async readRedis(key2){let result=await this.redis.read(key2);if(result.success)return result.data;throw new RateLimiterBackendError(result.error?String(result.error):"rate-limit backend read failed")}async check(params){if(!this.config.enabled)return{allowed:!0,remaining:-1,resetAt:0,limit:-1};if(this.isWhitelisted(params.ip))return{allowed:!0,remaining:-1,resetAt:0,limit:-1};if(this.isBlacklisted(params.ip))return this.logger.warn(`[RateLimit] Blacklisted IP: ${params.ip}`),{allowed:!1,remaining:0,resetAt:Date.now()+86400000,limit:0,retryAfter:86400};let key2=this.buildKey(params),limits=this.getLimits(params.category,params.authType),windowMs=this.parseTimeToMs(limits.window),blockKey=`${key2}:blocked`;try{let isBlocked=await this.readRedis(blockKey);if(isBlocked&&isBlocked.until>Date.now()){let retryAfter=Math.ceil((isBlocked.until-Date.now())/1000);return this.logger.warn(`[RateLimit] Blocked: ${key2}, retry after ${retryAfter}s`),{allowed:!1,remaining:0,resetAt:isBlocked.until,limit:limits.max,retryAfter}}if(this.config.strategy==="sliding-window")return await this.slidingWindowCheck(key2,limits.max,windowMs,limits.blockDuration);if(this.config.strategy==="fixed-window")return await this.fixedWindowCheck(key2,limits.max,windowMs,limits.blockDuration);return await this.tokenBucketCheck(key2,limits.max,windowMs)}catch(err){let failClosed=this.config.failClosed===!0||this.config.failClosed===void 0&&params.category==="auth";if(this.logger.error(`[RateLimit] Backend error for ${key2} \u2014 failing ${failClosed?"closed":"open"}`,{error:err instanceof Error?err.message:String(err),category:params.category}),failClosed)return{allowed:!1,remaining:0,resetAt:Date.now()+windowMs,limit:limits.max,retryAfter:Math.ceil(windowMs/1000)};return{allowed:!0,remaining:-1,resetAt:0,limit:limits.max}}}async slidingWindowCheck(key2,max,windowMs,blockDuration){let client=this.redis.getDirectClient?.();if(client)return this.slidingWindowAtomic(client,key2,max,windowMs,blockDuration);let now=Date.now(),windowStart=now-windowMs,dataKey=`${key2}:sw`,timestamps=(await this.readRedis(dataKey))?.timestamps||[];timestamps=timestamps.filter((ts)=>ts>windowStart);let count=timestamps.length,firstTimestamp=timestamps[0],resetAt=firstTimestamp!==void 0?firstTimestamp+windowMs:now+windowMs;if(count>=max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return timestamps.push(now),await this.redis.create(dataKey,{timestamps},Math.ceil(windowMs/1000)+1),{allowed:!0,remaining:max-timestamps.length,resetAt,limit:max}}async slidingWindowAtomic(client,key2,max,windowMs,blockDuration){let now=Date.now(),member=`${now}-${randomUUID4()}`,res=await client.eval(SLIDING_WINDOW_LUA,1,`${key2}:swz`,String(now),String(windowMs),String(max),member),allowed=Number(res?.[0])===1,count=Number(res?.[1]??0),resetAt=now+windowMs;if(!allowed){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil(windowMs/1000)}}return{allowed:!0,remaining:Math.max(0,max-count),resetAt,limit:max}}async fixedWindowAtomic(client,key2,max,windowMs,blockDuration){let now=Date.now(),windowId=Math.floor(now/windowMs),dataKey=`${key2}:fw:${windowId}`,resetAt=(windowId+1)*windowMs,count=await client.incr(dataKey);if(count===1)await client.pexpire(dataKey,windowMs+1000);if(count>max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return{allowed:!0,remaining:Math.max(0,max-count),resetAt,limit:max}}async fixedWindowCheck(key2,max,windowMs,blockDuration){let client=this.redis.getDirectClient?.();if(client)return this.fixedWindowAtomic(client,key2,max,windowMs,blockDuration);let now=Date.now(),windowId=Math.floor(now/windowMs),dataKey=`${key2}:fw:${windowId}`,resetAt=(windowId+1)*windowMs,count=(await this.readRedis(dataKey))?.count||0;if(count>=max){if(blockDuration){let blockMs=this.parseTimeToMs(blockDuration);await this.redis.create(`${key2}:blocked`,{until:now+blockMs},Math.ceil(blockMs/1000))}return{allowed:!1,remaining:0,resetAt,limit:max,retryAfter:Math.ceil((resetAt-now)/1000)}}return await this.redis.create(dataKey,{count:count+1},Math.ceil(windowMs/1000)+1),{allowed:!0,remaining:max-(count+1),resetAt,limit:max}}async tokenBucketCheck(key2,max,windowMs){let now=Date.now(),refillRate=max/windowMs,dataKey=`${key2}:tb`,data=await this.readRedis(dataKey),tokens=data?.tokens??max,lastRefill=data?.lastRefill??now,refill=(now-lastRefill)*refillRate;if(tokens=Math.min(max,tokens+refill),tokens<1){let waitTime=Math.ceil((1-tokens)/refillRate);return{allowed:!1,remaining:0,resetAt:now+waitTime,limit:max,retryAfter:Math.ceil(waitTime/1000)}}return tokens-=1,await this.redis.create(dataKey,{tokens,lastRefill:now},Math.ceil(windowMs/1000)*2),{allowed:!0,remaining:Math.floor(tokens),resetAt:now+windowMs,limit:max}}async decrement(params){if(!this.config.skipSuccessfulRequests)return;let key2=this.buildKey(params);if(this.config.strategy==="sliding-window"){let dataKey=`${key2}:sw`,data=await this.readRedis(dataKey);if(data?.timestamps?.length){data.timestamps.pop();let windowMs=this.parseTimeToMs(this.getLimits(params.category,params.authType).window);await this.redis.create(dataKey,data,Math.ceil(windowMs/1000)+1)}}else if(this.config.strategy==="fixed-window"){let windowMs=this.parseTimeToMs(this.getLimits(params.category,params.authType).window),windowId=Math.floor(Date.now()/windowMs),dataKey=`${key2}:fw:${windowId}`,data=await this.readRedis(dataKey);if(data?.count)await this.redis.create(dataKey,{count:data.count-1},Math.ceil(windowMs/1000)+1)}}getHeaders(result){let headers={};return headers[this.config.headers.remaining]=String(result.remaining),headers[this.config.headers.reset]=String(Math.ceil(result.resetAt/1000)),headers[this.config.headers.limit]=String(result.limit),headers}isEnabled(){return this.config.enabled}}var SLIDING_WINDOW_LUA=`
90
90
  local now = tonumber(ARGV[1])
91
91
  local windowMs = tonumber(ARGV[2])
92
92
  local max = tonumber(ARGV[3])
@@ -16,6 +16,16 @@ export type RedisLike = {
16
16
  * Redis-backed store (P3) — works across instances and survives restarts. State
17
17
  * is single-use: consume() deletes the key before returning, so a replayed
18
18
  * callback (or a double-submit) finds nothing.
19
+ *
20
+ * IMPORTANT: store the payload as an OBJECT and let the store layer handle
21
+ * serialization. Do NOT JSON.stringify here / JSON.parse on read. The RedisLike
22
+ * implementations already serialize internally — the direct ioredis store
23
+ * `JSON.stringify`s on write and `JSON.parse`s on read, and the Dapr store's
24
+ * `state.get` AUTO-PARSES JSON strings back into objects. A pre-stringify here
25
+ * therefore round-trips fine on the direct store (double-encode cancels out) but
26
+ * breaks on Dapr: `read` returns a parsed OBJECT, so a second `JSON.parse(object)`
27
+ * throws ("[object Object]") and consume returns null → every OAuth login fails
28
+ * with `invalid_state`. Passing the object through works on BOTH backends.
19
29
  */
20
30
  export declare const createRedisOAuthStateStore: (redis: RedisLike) => OAuthStateStore;
21
31
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.701",
3
+ "version": "0.9.702",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can \u00d6zcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",