TM-SGNL-iOS/SignalServiceKit/Jobs/JobRecords/DonationReceiptCredentialRedemptionJobRecord.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

196 lines
8.2 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import LibSignalClient
public final class DonationReceiptCredentialRedemptionJobRecord: JobRecord, FactoryInitializableFromRecordType {
override class var jobRecordType: JobRecordType { .donationReceiptCredentialRedemption }
public let paymentProcessor: String
public let paymentMethod: String?
public let receiptCredentialRequestContext: Data
public let receiptCredentialRequest: Data
private(set) var _receiptCredential: Data?
let _receiptCredentialPresentation: Data?
/// While this job now persists a `ReceiptCredential` we can convert to a
/// presentation on the fly, it historically persisted the presentation
/// directly. This method abstracts over those differences, which are not
/// interesting to callers!
func getReceiptCredentialPresentation() throws -> ReceiptCredentialPresentation? {
if let _receiptCredential {
return try DonationSubscriptionManager.generateReceiptCredentialPresentation(
receiptCredential: try ReceiptCredential(contents: [UInt8](_receiptCredential))
)
} else if let _receiptCredentialPresentation {
return try ReceiptCredentialPresentation(contents: [UInt8](_receiptCredentialPresentation))
}
return nil
}
public let isBoost: Bool
public let subscriberID: Data
public let targetSubscriptionLevel: UInt
public let priorSubscriptionLevel: UInt
public let isNewSubscription: Bool
public let shouldSuppressPaymentAlreadyRedeemed: Bool
public let boostPaymentIntentID: String
public let amount: Decimal?
public let currencyCode: String?
init(
paymentProcessor: String,
paymentMethod: String?,
receiptCredentialRequestContext: Data,
receiptCredentialRequest: Data,
subscriberID: Data,
targetSubscriptionLevel: UInt,
priorSubscriptionLevel: UInt,
isNewSubscription: Bool,
shouldSuppressPaymentAlreadyRedeemed: Bool,
isBoost: Bool,
amount: Decimal?,
currencyCode: String?,
boostPaymentIntentID: String
) {
self.paymentProcessor = paymentProcessor
self.paymentMethod = paymentMethod
self.receiptCredentialRequestContext = receiptCredentialRequestContext
self.receiptCredentialRequest = receiptCredentialRequest
self._receiptCredential = nil
self._receiptCredentialPresentation = nil
self.subscriberID = subscriberID
self.targetSubscriptionLevel = targetSubscriptionLevel
self.priorSubscriptionLevel = priorSubscriptionLevel
self.isNewSubscription = isNewSubscription
self.shouldSuppressPaymentAlreadyRedeemed = shouldSuppressPaymentAlreadyRedeemed
self.isBoost = isBoost
self.amount = amount
self.currencyCode = currencyCode
self.boostPaymentIntentID = boostPaymentIntentID
super.init(
failureCount: 0,
status: .ready
)
}
#if TESTABLE_BUILD
init(
paymentProcessor: String,
paymentMethod: String?,
receiptCredentialRequestContext: Data,
receiptCredentialRequest: Data,
receiptCredential: Data?,
receiptCredentialPresentation: Data?,
subscriberID: Data,
targetSubscriptionLevel: UInt,
priorSubscriptionLevel: UInt,
isNewSubscription: Bool,
shouldSuppressPaymentAlreadyRedeemed: Bool,
isBoost: Bool,
amount: Decimal?,
currencyCode: String?,
boostPaymentIntentID: String,
failureCount: UInt,
status: Status
) {
self.paymentProcessor = paymentProcessor
self.paymentMethod = paymentMethod
self.receiptCredentialRequestContext = receiptCredentialRequestContext
self.receiptCredentialRequest = receiptCredentialRequest
self._receiptCredential = receiptCredential
self._receiptCredentialPresentation = receiptCredentialPresentation
self.subscriberID = subscriberID
self.targetSubscriptionLevel = targetSubscriptionLevel
self.priorSubscriptionLevel = priorSubscriptionLevel
self.isNewSubscription = isNewSubscription
self.shouldSuppressPaymentAlreadyRedeemed = shouldSuppressPaymentAlreadyRedeemed
self.isBoost = isBoost
self.amount = amount
self.currencyCode = currencyCode
self.boostPaymentIntentID = boostPaymentIntentID
super.init(
failureCount: failureCount,
status: status
)
}
#endif
required init(forRecordTypeFactoryInitializationFrom decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
paymentProcessor = try container.decode(String.self, forKey: .paymentProcessor)
paymentMethod = try container.decodeIfPresent(String.self, forKey: .paymentMethod)
receiptCredentialRequestContext = try container.decode(Data.self, forKey: .receiptCredentialRequestContext)
receiptCredentialRequest = try container.decode(Data.self, forKey: .receiptCredentialRequest)
_receiptCredential = try container.decodeIfPresent(Data.self, forKey: ._receiptCredential)
_receiptCredentialPresentation = try container.decodeIfPresent(Data.self, forKey: ._receiptCredentialPresentation)
isBoost = try container.decode(Bool.self, forKey: .isBoost)
subscriberID = try container.decode(Data.self, forKey: .subscriberID)
targetSubscriptionLevel = try container.decode(UInt.self, forKey: .targetSubscriptionLevel)
priorSubscriptionLevel = try container.decode(UInt.self, forKey: .priorSubscriptionLevel)
isNewSubscription = try container.decodeIfPresent(Bool.self, forKey: .isNewSubscription) ?? true
shouldSuppressPaymentAlreadyRedeemed = try container.decodeIfPresent(Bool.self, forKey: .shouldSuppressPaymentAlreadyRedeemed) ?? false
boostPaymentIntentID = try container.decode(String.self, forKey: .boostPaymentIntentID)
amount = try container.decodeIfPresent(
Data.self,
forKey: .amount
).map { amountData in
return try LegacySDSSerializer().deserializeLegacySDSData(
amountData,
propertyName: "amount"
)
}
currencyCode = try container.decodeIfPresent(String.self, forKey: .currencyCode)
try super.init(baseClassDuringFactoryInitializationFrom: container.superDecoder())
}
public override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try super.encode(to: container.superEncoder())
try container.encode(paymentProcessor, forKey: .paymentProcessor)
try container.encodeIfPresent(paymentMethod, forKey: .paymentMethod)
try container.encode(receiptCredentialRequestContext, forKey: .receiptCredentialRequestContext)
try container.encode(receiptCredentialRequest, forKey: .receiptCredentialRequest)
try container.encodeIfPresent(_receiptCredential, forKey: ._receiptCredential)
try container.encodeIfPresent(_receiptCredentialPresentation, forKey: ._receiptCredentialPresentation)
try container.encode(subscriberID, forKey: .subscriberID)
try container.encode(targetSubscriptionLevel, forKey: .targetSubscriptionLevel)
try container.encode(priorSubscriptionLevel, forKey: .priorSubscriptionLevel)
try container.encode(isNewSubscription, forKey: .isNewSubscription)
try container.encode(shouldSuppressPaymentAlreadyRedeemed, forKey: .shouldSuppressPaymentAlreadyRedeemed)
try container.encode(isBoost, forKey: .isBoost)
try container.encodeIfPresent(
LegacySDSSerializer().serializeAsLegacySDSData(property: amount),
forKey: .amount
)
try container.encodeIfPresent(currencyCode, forKey: .currencyCode)
try container.encode(boostPaymentIntentID, forKey: .boostPaymentIntentID)
}
// MARK: Update
func setReceiptCredential(_ receiptCredential: ReceiptCredential, tx: SDSAnyWriteTransaction) {
anyUpdate(transaction: tx) { record in
record._receiptCredential = receiptCredential.serialize().asData
}
}
}