// // Copyright RevenueCat Inc. All Rights Reserved. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://opensource.org/licenses/MIT // // DefaultCheckpointWorkflowResolver+Offering.swift // // Created by Rosie Watson on 10/30/26. import Foundation extension DefaultCheckpointWorkflowResolver { func resolveOfferingStep( _ rule: CheckpointRule, step: WorkflowStep, in workflowData: WorkflowDataResult, checkpointIdentifier: String, traceID: String ) async -> (resolution: CheckpointResolution, stepEvents: [WorkflowEvent]) { guard workflowData.workflow.steps.count == 1 else { return (Self.unservable(rule, reason: "an offering step cannot be mixed with other steps"), []) } // Validated before the step starts, so a step that was never servable emits nothing. guard let offeringID = step.offeringIdentifier else { return (Self.unservable(rule, reason: "the offering step has no valid offering identifier"), []) } return await Self.trackingStepEvents(for: step, in: workflowData, traceID: traceID) { guard let offering = await self.offering(identifier: offeringID, for: rule) else { return .noAction(.configurationUnavailable) } return .matchedOffering( offering.withPresentedCheckpointIdentifier(checkpointIdentifier, traceID: traceID) .withPresentedWorkflowContext( workflowId: workflowData.workflow.id, stepId: step.id, blobRef: workflowData.workflowBlobRef ), traceID: traceID ) } } /// Loads the offerings and picks `identifier` out of them, logging why the rule can't be served when it /// isn't there. private func offering( identifier: String, for rule: CheckpointRule ) async -> Offering? { guard let offerings = await self.loadOfferings() else { return nil } guard let offering = offerings.offering(identifier: identifier) else { Self.unservable(rule, reason: "offering '\(identifier)' is unavailable") return nil } return offering } }