mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-20 00:35:47 +01:00
Convert the libsignal-service JSON models to Kotlin.
This commit is contained in:
@@ -15,7 +15,7 @@ import org.whispersystems.signalservice.internal.push.DeviceInfoList
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyEntity
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyResponse
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyResponseItem
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationLockFailure
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataJson
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
import org.whispersystems.signalservice.internal.push.WhoAmIResponse
|
||||
@@ -26,18 +26,16 @@ import java.security.SecureRandom
|
||||
*/
|
||||
object MockProvider {
|
||||
|
||||
val lockedFailure = PushServiceSocket.RegistrationLockFailure().apply {
|
||||
svr1Credentials = AuthCredentials.create("username", "password")
|
||||
val lockedFailure = RegistrationLockFailure(
|
||||
svr1Credentials = AuthCredentials.create("username", "password"),
|
||||
svr2Credentials = AuthCredentials.create("username", "password")
|
||||
}
|
||||
)
|
||||
|
||||
val primaryOnlyDeviceList = DeviceInfoList().apply {
|
||||
val primaryOnlyDeviceList = DeviceInfoList(
|
||||
devices = listOf(
|
||||
DeviceInfo().apply {
|
||||
id = 1
|
||||
}
|
||||
DeviceInfo(id = 1)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val sessionMetadataJson = RegistrationSessionMetadataJson(
|
||||
id = "asdfasdfasdfasdf",
|
||||
@@ -50,11 +48,11 @@ object MockProvider {
|
||||
)
|
||||
|
||||
fun createVerifyAccountResponse(aci: ServiceId, newPni: ServiceId): VerifyAccountResponse {
|
||||
return VerifyAccountResponse().apply {
|
||||
uuid = aci.toString()
|
||||
pni = newPni.toString()
|
||||
return VerifyAccountResponse(
|
||||
uuid = aci.toString(),
|
||||
pni = newPni.toString(),
|
||||
storageCapable = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun createWhoAmIResponse(aci: ServiceId, pni: ServiceId, e164: String): WhoAmIResponse {
|
||||
@@ -69,16 +67,16 @@ object MockProvider {
|
||||
val signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), identity.privateKey)
|
||||
val oneTimePreKey = PreKeyRecord(SecureRandom().nextInt(Medium.MAX_VALUE), ECKeyPair.generate())
|
||||
|
||||
val device = PreKeyResponseItem().apply {
|
||||
this.deviceId = deviceId
|
||||
registrationId = KeyHelper.generateRegistrationId(false)
|
||||
signedPreKey = SignedPreKeyEntity(signedPreKeyRecord.id.toLong(), signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature)
|
||||
val device = PreKeyResponseItem(
|
||||
deviceId = deviceId,
|
||||
registrationId = KeyHelper.generateRegistrationId(false),
|
||||
signedPreKey = SignedPreKeyEntity(signedPreKeyRecord.id.toLong(), signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature),
|
||||
preKey = PreKeyEntity(oneTimePreKey.id.toLong(), oneTimePreKey.keyPair.publicKey)
|
||||
}
|
||||
)
|
||||
|
||||
return PreKeyResponse().apply {
|
||||
identityKey = identity.publicKey
|
||||
return PreKeyResponse(
|
||||
identityKey = identity.publicKey,
|
||||
devices = listOf(device)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ class MessageBackupsFlowViewModel(
|
||||
}
|
||||
|
||||
activeSubscription.runIfSuccessful { subscription ->
|
||||
if (subscription.willCancelAtPeriodEnd()) {
|
||||
if (subscription.willCancelAtPeriodEnd) {
|
||||
Log.d(TAG, "Active subscription is cancelled. Clearing tier.")
|
||||
internalStateFlow.update {
|
||||
it.copy(
|
||||
|
||||
@@ -123,7 +123,7 @@ object Badges {
|
||||
uriAndDensity.first,
|
||||
uriAndDensity.second,
|
||||
serviceBadge.expiration?.let { getTimestamp(it) } ?: 0,
|
||||
serviceBadge.isVisible,
|
||||
serviceBadge.visible,
|
||||
TimeUnit.SECONDS.toMillis(serviceBadge.duration)
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ class BadgesOverviewViewModel(
|
||||
RecurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION),
|
||||
RecurringInAppPaymentRepository.getSubscriptions()
|
||||
) { active, all ->
|
||||
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) {
|
||||
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd == true) {
|
||||
Optional.ofNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
|
||||
} else {
|
||||
Optional.empty()
|
||||
|
||||
+5
-5
@@ -297,13 +297,13 @@ class BackupStateObserver(
|
||||
}
|
||||
}
|
||||
|
||||
val signalServiceSubscriptionIsActiveAndWillRenew = activeSubscription?.isActive == true && (!activeSubscription.isCanceled || activeSubscription.willCancelAtPeriodEnd())
|
||||
val signalServiceSubscriptionIsActiveAndWillRenew = activeSubscription?.isActive == true && (!activeSubscription.isCanceled || activeSubscription.willCancelAtPeriodEnd)
|
||||
|
||||
Log.d(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] signalServiceSubscriptionIsActiveAndWillRenew: $signalServiceSubscriptionIsActiveAndWillRenew")
|
||||
|
||||
when {
|
||||
signalServiceSubscriptionIsActiveAndWillRenew && !googlePlayBillingSubscriptionIsActiveAndWillRenew -> {
|
||||
val type = buildPaidTypeFromSubscription(activeSubscription.activeSubscription)
|
||||
val type = buildPaidTypeFromSubscription(activeSubscription.activeSubscription!!)
|
||||
|
||||
if (type == null) {
|
||||
Log.d(TAG, "[getNetworkBackupState][subscriptionMismatchDetected] failed to load backup configuration. Likely a network error.")
|
||||
@@ -313,8 +313,8 @@ class BackupStateObserver(
|
||||
Log.d(TAG, "[getNetworkBackupState][subscriptionMismatchDetected] found a subscription mismatch and successfully loaded configuration.")
|
||||
return BackupState.SubscriptionMismatchMissingGooglePlay(
|
||||
messageBackupsType = type,
|
||||
renewalTime = activeSubscription.activeSubscription.endOfCurrentPeriod.seconds,
|
||||
isBilledThroughOtherStore = InAppPaymentsRepository.isBackupBilledThroughOtherStore(activeSubscription.activeSubscription)
|
||||
renewalTime = activeSubscription.activeSubscription!!.endOfCurrentPeriod.seconds,
|
||||
isBilledThroughOtherStore = InAppPaymentsRepository.isBackupBilledThroughOtherStore(activeSubscription.activeSubscription!!)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ class BackupStateObserver(
|
||||
getStateOnError()
|
||||
} else {
|
||||
when {
|
||||
(subscription.isCanceled || subscription.willCancelAtPeriodEnd()) && subscription.isActive -> {
|
||||
(subscription.isCanceled || subscription.willCancelAtPeriodEnd) && subscription.isActive -> {
|
||||
Log.d(TAG, "[getPaidBackupState] Found a canceled subscription.")
|
||||
InAppPaymentsRepository.updateBackupInAppPaymentWithCancelation(activeSubscription.successOrThrow())
|
||||
|
||||
|
||||
+7
-7
@@ -334,9 +334,9 @@ class ChangeNumberRepository(
|
||||
return ChangeNumberResult.from(
|
||||
result.map { accountRegistrationResponse: VerifyAccountResponse ->
|
||||
NumberChangeResult(
|
||||
uuid = accountRegistrationResponse.uuid,
|
||||
pni = accountRegistrationResponse.pni,
|
||||
number = accountRegistrationResponse.number
|
||||
uuid = accountRegistrationResponse.uuid!!,
|
||||
pni = accountRegistrationResponse.pni!!,
|
||||
number = accountRegistrationResponse.number!!
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -353,11 +353,11 @@ class ChangeNumberRepository(
|
||||
if (whoAmI.number == newE164 && whoAmI.pni != null) {
|
||||
Log.w(TAG, "Change number request did not succeed, but whoami reports the new number is already active. Treating the change as successful.")
|
||||
NetworkResult.Success(
|
||||
VerifyAccountResponse().apply {
|
||||
uuid = whoAmI.aci
|
||||
pni = whoAmI.pni
|
||||
VerifyAccountResponse(
|
||||
uuid = whoAmI.aci,
|
||||
pni = whoAmI.pni,
|
||||
number = whoAmI.number
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Log.i(TAG, "Change number request did not succeed and whoami does not report the new number; treating as a genuine failure.")
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.registration.data.network.RegistrationResult
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException
|
||||
import org.whispersystems.signalservice.api.svr.Svr3Credentials
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket.RegistrationLockFailure
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationLockFailure
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class InternalPendingOneTimeDonationConfigurationViewModel : ViewModel() {
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { config ->
|
||||
val badge = Badges.fromServiceBadge(config.levels.values.first().badge)
|
||||
val badge = Badges.fromServiceBadge(config.levels.values.first().badge!!)
|
||||
state.value = state.value.copy(badge = Badges.toDatabaseBadge(badge))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class InternalDonorErrorConfigurationViewModel : ViewModel() {
|
||||
val configuration = SignalNetwork.donationsService.getDonationsConfiguration(Locale.getDefault()).toNetworkResult().successOrNull() ?: return@launch
|
||||
val giftBadges = configuration.getGiftBadges()
|
||||
val boostBadges = configuration.getBoostBadges()
|
||||
val subscriptionBadges = configuration.getSubscriptionLevels().values.map { Badges.fromServiceBadge(it.badge) }
|
||||
val subscriptionBadges = configuration.getSubscriptionLevels().values.map { Badges.fromServiceBadge(it.badge!!) }
|
||||
|
||||
store.update { it.copy(badges = giftBadges + boostBadges + subscriptionBadges) }
|
||||
}
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@ import org.signal.core.util.money.PlatformCurrencyUtil
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.BOOST_LEVEL
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.GIFT_LEVEL
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.BOOST_LEVEL
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.GIFT_LEVEL
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.SUBSCRIPTION_LEVELS
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.LevelConfiguration
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.SUBSCRIPTION_LEVELS
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
@@ -79,7 +79,7 @@ fun SubscriptionsConfiguration.getBoostAmounts(paymentMethodAvailability: Paymen
|
||||
|
||||
fun SubscriptionsConfiguration.getBadge(level: Int): Badge {
|
||||
require(level == GIFT_LEVEL || level == BOOST_LEVEL || SUBSCRIPTION_LEVELS.contains(level))
|
||||
return Badges.fromServiceBadge(levels[level]!!.badge)
|
||||
return Badges.fromServiceBadge(levels[level]!!.badge!!)
|
||||
}
|
||||
|
||||
fun SubscriptionsConfiguration.getSubscriptionLevels(): Map<Int, LevelConfiguration> {
|
||||
|
||||
+2
-2
@@ -98,7 +98,7 @@ object InAppPaymentsRepository {
|
||||
* This operation will only be performed if we find a latest payment for the given subscriber id in the END state without cancelation data.
|
||||
*/
|
||||
fun updateInAppPaymentWithCancelation(activeSubscription: ActiveSubscription, subscriberType: InAppPaymentSubscriberRecord.Type) {
|
||||
if (activeSubscription.isCanceled || (subscriberType == InAppPaymentSubscriberRecord.Type.BACKUP && activeSubscription.willCancelAtPeriodEnd()) || activeSubscription.isFailedPayment) {
|
||||
if (activeSubscription.isCanceled || (subscriberType == InAppPaymentSubscriberRecord.Type.BACKUP && activeSubscription.willCancelAtPeriodEnd) || activeSubscription.isFailedPayment) {
|
||||
writeCancelation(subscriberType, activeSubscription.chargeFailure)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ object InAppPaymentsRepository {
|
||||
* This operation will only be performed if we find a latest payment for the given subscriber id in the END state with cancelation data
|
||||
*/
|
||||
fun clearCancelation(activeSubscription: ActiveSubscription) {
|
||||
if (!activeSubscription.isCanceled && !activeSubscription.willCancelAtPeriodEnd()) {
|
||||
if (!activeSubscription.isCanceled && !activeSubscription.willCancelAtPeriodEnd) {
|
||||
val subscriber = getSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP) ?: return
|
||||
|
||||
val latestPayment = SignalDatabase.inAppPayments.getLatestBySubscriberId(subscriber.subscriberId) ?: return
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ object RecurringInAppPaymentRepository {
|
||||
|
||||
response.result.ifPresent { result ->
|
||||
val lastEndOfPeriod = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(type.inAppPaymentType)?.endOfPeriodSeconds ?: 0L
|
||||
if (result.isActive && result.activeSubscription.endOfCurrentPeriod > lastEndOfPeriod) {
|
||||
if (result.isActive && result.activeSubscription!!.endOfCurrentPeriod > lastEndOfPeriod) {
|
||||
InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds)
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ object RecurringInAppPaymentRepository {
|
||||
Subscription(
|
||||
id = level.toString(),
|
||||
level = level,
|
||||
badge = Badges.fromServiceBadge(levelConfig.badge),
|
||||
badge = Badges.fromServiceBadge(levelConfig.badge!!),
|
||||
prices = config.getSubscriptionAmounts(level)
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class TerminalDonationRepository(
|
||||
.fromCallable { donationsService.getDonationsConfiguration(Locale.getDefault()) }
|
||||
.flatMap { it.flattenResult() }
|
||||
.map { it.levels[terminalDonation.level.toInt()]!! }
|
||||
.map { Badges.fromServiceBadge(it.badge) }
|
||||
.map { Badges.fromServiceBadge(it.badge!!) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ data class DonateToSignalState(
|
||||
val isSubscriptionInProgress: Boolean = _activeSubscription?.isInProgress == true
|
||||
val activeLevel: Int? = _activeSubscription?.activeSubscription?.level
|
||||
val activeSubscription: ActiveSubscription.Subscription? = _activeSubscription?.activeSubscription
|
||||
val isActiveSubscriptionEnding: Boolean = _activeSubscription?.isActive == true && _activeSubscription.activeSubscription.willCancelAtPeriodEnd()
|
||||
val isActiveSubscriptionEnding: Boolean = _activeSubscription?.isActive == true && _activeSubscription.activeSubscription?.willCancelAtPeriodEnd == true
|
||||
val renewalTimestamp = TimeUnit.SECONDS.toMillis(activeSubscription?.endOfCurrentPeriod ?: 0L)
|
||||
val isSelectionValid = selectedSubscription != null && (!isSubscriptionActive || selectedSubscription.level != activeSubscription?.level)
|
||||
}
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ class DonateToSignalViewModel(
|
||||
|
||||
private fun resolveSelectedSubscription(activeSubscription: ActiveSubscription, subscriptions: List<Subscription>): Subscription? {
|
||||
return if (activeSubscription.isActive) {
|
||||
subscriptions.firstOrNull { it.level == activeSubscription.activeSubscription.level }
|
||||
subscriptions.firstOrNull { it.level == activeSubscription.activeSubscription!!.level }
|
||||
} else {
|
||||
subscriptions.firstOrNull()
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ object GatewaySelectorRepository {
|
||||
|
||||
GatewayConfiguration(
|
||||
availableGateways = available,
|
||||
sepaEuroMaximum = if (configuration.sepaMaximumEuros != null) FiatMoney(configuration.sepaMaximumEuros, CurrencyUtil.EURO) else null
|
||||
sepaEuroMaximum = configuration.sepaMaximumEuros?.let { FiatMoney(it, CurrencyUtil.EURO) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -18,14 +18,15 @@ fun InAppPaymentProcessorError.toDonationError(
|
||||
return when (processor) {
|
||||
ActiveSubscription.Processor.STRIPE -> {
|
||||
check(method is PaymentSourceType.Stripe)
|
||||
val declineCode = StripeDeclineCode.getFromCode(chargeFailure.code)
|
||||
val failureCode = StripeFailureCode.getFromCode(chargeFailure.code)
|
||||
val chargeFailureCode = chargeFailure.code
|
||||
val declineCode = StripeDeclineCode.getFromCode(chargeFailureCode)
|
||||
val failureCode = StripeFailureCode.getFromCode(chargeFailureCode)
|
||||
if (declineCode.isKnown()) {
|
||||
DonationError.PaymentSetupError.StripeDeclinedError(source, this, declineCode, method)
|
||||
} else if (failureCode.isKnown) {
|
||||
DonationError.PaymentSetupError.StripeFailureCodeError(source, this, failureCode, method)
|
||||
} else if (chargeFailure.code != null) {
|
||||
DonationError.PaymentSetupError.StripeCodedError(source, this, chargeFailure.code)
|
||||
} else if (chargeFailureCode != null) {
|
||||
DonationError.PaymentSetupError.StripeCodedError(source, this, chargeFailureCode)
|
||||
} else {
|
||||
DonationError.PaymentSetupError.GenericError(source, this)
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class DonationReceiptListRepository {
|
||||
val subBadges = config.getSubscriptionLevels().map {
|
||||
DonationReceiptBadge(
|
||||
level = it.key,
|
||||
badge = Badges.fromServiceBadge(it.value.badge),
|
||||
badge = Badges.fromServiceBadge(it.value.badge!!),
|
||||
type = InAppPaymentReceiptRecord.Type.RECURRING_DONATION
|
||||
)
|
||||
}
|
||||
|
||||
@@ -432,9 +432,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
@JvmStatic
|
||||
fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long {
|
||||
var value: Long = 0
|
||||
value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStorageServiceEncryptionV2).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.USERNAME_SYNC_MESSAGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isUsernameSyncMessages).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.OPTIONAL_PHONE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isOptionalPhoneNumber).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.storageServiceEncryptionV2).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.USERNAME_SYNC_MESSAGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.usernameSyncMessages).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.OPTIONAL_PHONE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.optionalPhoneNumber).serialize().toLong())
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
|
||||
checkForFailedOrCanceledSubscriptionState(activeSubscription)
|
||||
|
||||
val isSignalSubscriptionFailedOrCanceled = activeSubscription?.willCancelAtPeriodEnd() == true
|
||||
val isSignalSubscriptionFailedOrCanceled = activeSubscription?.willCancelAtPeriodEnd == true
|
||||
if (hasActiveSignalSubscription && !isSignalSubscriptionFailedOrCanceled) {
|
||||
checkAndSynchronizeZkCredentialTierWithStoredLocalTier()
|
||||
}
|
||||
@@ -256,7 +256,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
* the "download your data" notifier sheet.
|
||||
*/
|
||||
private fun checkForFailedOrCanceledSubscriptionState(activeSubscription: ActiveSubscription?) {
|
||||
if (activeSubscription?.willCancelAtPeriodEnd() == true && activeSubscription.activeSubscription != null) {
|
||||
if (activeSubscription?.willCancelAtPeriodEnd == true && activeSubscription.activeSubscription != null) {
|
||||
Log.i(TAG, "Subscription either has a payment failure or has been canceled.")
|
||||
|
||||
val response = SignalNetwork.accountApi.whoAmI()
|
||||
|
||||
@@ -300,7 +300,7 @@ class InAppPaymentKeepAliveJob private constructor(
|
||||
info(type, "Failed to load subscription configuration for level ${subscription.level} for type $type")
|
||||
null
|
||||
} else {
|
||||
Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge))
|
||||
Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge!!))
|
||||
}
|
||||
} else {
|
||||
warn(TAG, "Failed to load configuration while processing $type")
|
||||
|
||||
@@ -289,15 +289,15 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
|
||||
|
||||
is MessageService.SendError.ChallengeRequired -> {
|
||||
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Challenge required (options=${error.options})", error)
|
||||
val proofResponse = ProofRequiredResponse().apply {
|
||||
token = error.token
|
||||
val proofResponse = ProofRequiredResponse(
|
||||
token = error.token,
|
||||
options = error.options.map {
|
||||
when (it) {
|
||||
ChallengeOption.PUSH_CHALLENGE -> "pushChallenge"
|
||||
ChallengeOption.CAPTCHA -> "captcha"
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
val proofException = ProofRequiredException(proofResponse, error.retryAfter?.inWholeSeconds ?: 0L)
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(threadId)
|
||||
when (ProofRequiredExceptionHandler.handle(context, proofException, threadRecipient, threadId, messageId)) {
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ class RefreshDonationSubscriptionStatusJob private constructor(parameters: Param
|
||||
val activeSubscription = SignalNetwork.donationsService.getSubscription(subscriber.subscriberId).resultOrThrow
|
||||
|
||||
if (activeSubscription.isActive) {
|
||||
val endOfCurrentPeriod = activeSubscription.activeSubscription.endOfCurrentPeriod
|
||||
val endOfCurrentPeriod = activeSubscription.activeSubscription!!.endOfCurrentPeriod
|
||||
if (endOfCurrentPeriod > SignalStore.inAppPayments.getLastEndOfPeriod()) {
|
||||
Log.i(TAG, "Server reports active subscription with newer end-of-period. Updating local state.")
|
||||
SignalStore.inAppPayments.setLastEndOfPeriod(endOfCurrentPeriod)
|
||||
|
||||
@@ -108,10 +108,10 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
|
||||
val profile = profileAndCredential.getProfile()
|
||||
|
||||
if (Util.isEmpty(profile.getName()) &&
|
||||
Util.isEmpty(profile.getAvatar()) &&
|
||||
Util.isEmpty(profile.getAbout()) &&
|
||||
Util.isEmpty(profile.getAboutEmoji())
|
||||
if (Util.isEmpty(profile.name) &&
|
||||
Util.isEmpty(profile.avatar) &&
|
||||
Util.isEmpty(profile.about) &&
|
||||
Util.isEmpty(profile.aboutEmoji)
|
||||
) {
|
||||
Log.w(TAG, "The profile we retrieved was empty! Ignoring it.")
|
||||
|
||||
@@ -125,13 +125,13 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
return
|
||||
}
|
||||
|
||||
setProfileName(profile.getName())
|
||||
setProfileAbout(profile.getAbout(), profile.getAboutEmoji())
|
||||
setProfileAvatar(profile.getAvatar())
|
||||
setProfileCapabilities(profile.getCapabilities())
|
||||
setProfileBadges(profile.getBadges())
|
||||
ensureUnidentifiedAccessCorrect(profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess())
|
||||
ensurePhoneNumberSharingIsCorrect(profile.getPhoneNumberSharing())
|
||||
setProfileName(profile.name)
|
||||
setProfileAbout(profile.about, profile.aboutEmoji)
|
||||
setProfileAvatar(profile.avatar)
|
||||
setProfileCapabilities(profile.capabilities)
|
||||
setProfileBadges(profile.badges)
|
||||
ensureUnidentifiedAccessCorrect(profile.unidentifiedAccess, profile.unrestrictedUnidentifiedAccess)
|
||||
ensurePhoneNumberSharingIsCorrect(profile.phoneNumberSharing)
|
||||
|
||||
profileAndCredential.getExpiringProfileKeyCredential()
|
||||
.ifPresent { setExpiringProfileKeyCredential(self, ProfileKeyUtil.getSelfProfileKey(), it) }
|
||||
@@ -287,8 +287,8 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
.toSet()
|
||||
|
||||
val remoteDonorBadgeIds = badges
|
||||
.filter { it.getCategory() == Badge.Category.Donor.code }
|
||||
.map { it.getId() }
|
||||
.filter { it.category == Badge.Category.Donor.code }
|
||||
.map { it.id }
|
||||
.toSet()
|
||||
|
||||
val remoteHasSubscriptionBadges = remoteDonorBadgeIds.any { isSubscription(it) }
|
||||
@@ -320,13 +320,14 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
|
||||
if (response.getResult().isPresent()) {
|
||||
val activeSubscription = response.getResult().get()
|
||||
if (activeSubscription.isFailedPayment()) {
|
||||
if (activeSubscription.isFailedPayment) {
|
||||
Log.d(TAG, "Unexpected expiry due to payment failure.", true)
|
||||
isDueToPaymentFailure = true
|
||||
}
|
||||
|
||||
if (activeSubscription.getChargeFailure() != null) {
|
||||
Log.d(TAG, "Active payment contains a charge failure: " + activeSubscription.getChargeFailure().getCode(), true)
|
||||
val chargeFailure = activeSubscription.chargeFailure
|
||||
if (chargeFailure != null) {
|
||||
Log.d(TAG, "Active payment contains a charge failure: " + chargeFailure.code, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +377,8 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
SignalStore.inAppPayments.setExpiredGiftBadge(null)
|
||||
}
|
||||
|
||||
val userHasVisibleBadges = badges.any { it.isVisible() }
|
||||
val userHasInvisibleBadges = badges.any { !it.isVisible() }
|
||||
val userHasVisibleBadges = badges.any { it.visible }
|
||||
val userHasInvisibleBadges = badges.any { !it.visible }
|
||||
|
||||
val appBadges = badges.map { Badges.fromServiceBadge(it) }
|
||||
|
||||
|
||||
@@ -245,11 +245,12 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val
|
||||
return true
|
||||
}
|
||||
|
||||
if (localRecipientRecord.badges != remoteProfile.badges.map { Badges.fromServiceBadge(it) }) {
|
||||
if (localRecipientRecord.badges != remoteProfile.badges.orEmpty().map { Badges.fromServiceBadge(it) }) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (localRecipientRecord.capabilities.rawBits != maskCapabilitiesToLong(remoteProfile.capabilities)) {
|
||||
val remoteCapabilities = remoteProfile.capabilities
|
||||
if (remoteCapabilities != null && localRecipientRecord.capabilities.rawBits != maskCapabilitiesToLong(remoteCapabilities)) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -257,7 +258,7 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val
|
||||
val accessMode = deriveUnidentifiedAccessMode(
|
||||
profileKey = profileKey,
|
||||
unidentifiedAccessVerifier = remoteProfile.unidentifiedAccess,
|
||||
unrestrictedUnidentifiedAccess = remoteProfile.isUnrestrictedUnidentifiedAccess
|
||||
unrestrictedUnidentifiedAccess = remoteProfile.unrestrictedUnidentifiedAccess
|
||||
)
|
||||
|
||||
if (localRecipientRecord.sealedSenderAccessMode != accessMode) {
|
||||
@@ -297,7 +298,7 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val
|
||||
val recipientProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey)
|
||||
|
||||
val badges = profile.badges?.map { Badges.fromServiceBadge(it) }
|
||||
val accessMode = deriveUnidentifiedAccessMode(recipientProfileKey, profile.unidentifiedAccess, profile.isUnrestrictedUnidentifiedAccess)
|
||||
val accessMode = deriveUnidentifiedAccessMode(recipientProfileKey, profile.unidentifiedAccess, profile.unrestrictedUnidentifiedAccess)
|
||||
|
||||
if (badges != null && badges.size != recipient.badges.size) {
|
||||
Log.i(TAG, "Likely change in badges for ${recipient.id}. Going from ${recipient.badges.size} badge(s) to ${badges.size}.")
|
||||
|
||||
@@ -88,26 +88,26 @@ object LinkDeviceRepository {
|
||||
}
|
||||
|
||||
fun WaitForLinkedDeviceResponse.getPlaintextDevice(): Device {
|
||||
val response = this
|
||||
return DeviceInfo().apply {
|
||||
id = response.id
|
||||
name = response.name
|
||||
lastSeen = response.lastSeen
|
||||
registrationId = response.registrationId
|
||||
createdAtCiphertext = response.createdAtCiphertext
|
||||
}.toLocalDevice()
|
||||
return DeviceInfo(
|
||||
id = id,
|
||||
name = name,
|
||||
lastSeen = lastSeen,
|
||||
registrationId = registrationId,
|
||||
createdAtCiphertext = createdAtCiphertext
|
||||
).toLocalDevice()
|
||||
}
|
||||
|
||||
private fun DeviceInfo.toLocalDevice(): Device {
|
||||
val createdAt = this.getPlaintextCreatedAt()
|
||||
val defaultDevice = Device(getId(), getName(), createdAt, getLastSeen(), getRegistrationId())
|
||||
val encodedName = name
|
||||
val defaultDevice = Device(id, encodedName, createdAt, lastSeen, registrationId)
|
||||
try {
|
||||
if (getName().isNullOrEmpty() || getName().length < 4) {
|
||||
if (encodedName.isNullOrEmpty() || encodedName.length < 4) {
|
||||
Log.w(TAG, "Invalid DeviceInfo name.")
|
||||
return defaultDevice
|
||||
}
|
||||
|
||||
val deviceName = DeviceName.ADAPTER.decode(Base64.decode(getName()))
|
||||
val deviceName = DeviceName.ADAPTER.decode(Base64.decode(encodedName))
|
||||
if (deviceName.ciphertext == null || deviceName.ephemeralPublic == null || deviceName.syntheticIv == null) {
|
||||
Log.w(TAG, "Got a DeviceName that wasn't properly populated.")
|
||||
return defaultDevice
|
||||
@@ -119,7 +119,7 @@ object LinkDeviceRepository {
|
||||
return defaultDevice
|
||||
}
|
||||
|
||||
return Device(getId(), String(plaintext), createdAt, getLastSeen(), getRegistrationId())
|
||||
return Device(id, String(plaintext), createdAt, lastSeen, registrationId)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed while reading the protobuf.", e)
|
||||
}
|
||||
@@ -156,9 +156,9 @@ object LinkDeviceRepository {
|
||||
|
||||
private fun DeviceInfo.getPlaintextCreatedAt(): Long? {
|
||||
return try {
|
||||
val associatedData = byteArrayOf(getId().toByte()) + this.getRegistrationId().toByteArray()
|
||||
val associatedData = byteArrayOf(id.toByte()) + registrationId.toByteArray()
|
||||
val createdAtPlaintext = SignalStore.account.aciIdentityKey.privateKey.open(
|
||||
ciphertext = Base64.decode(this.getCreatedAtCiphertext().toByteArray()),
|
||||
ciphertext = Base64.decode(createdAtCiphertext!!.toByteArray()),
|
||||
info = DECRYPTION_INFO,
|
||||
associatedData = associatedData
|
||||
)
|
||||
|
||||
+3
-3
@@ -464,10 +464,10 @@ object RegistrationRepository {
|
||||
val result: NetworkResult<AccountRegistrationResult> = api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
.map { accountRegistrationResponse: VerifyAccountResponse ->
|
||||
AccountRegistrationResult(
|
||||
uuid = accountRegistrationResponse.uuid,
|
||||
pni = accountRegistrationResponse.pni,
|
||||
uuid = accountRegistrationResponse.uuid!!,
|
||||
pni = accountRegistrationResponse.pni!!,
|
||||
storageCapable = accountRegistrationResponse.storageCapable,
|
||||
number = accountRegistrationResponse.number,
|
||||
number = accountRegistrationResponse.number!!,
|
||||
masterKey = masterKey,
|
||||
pin = pin,
|
||||
aciPreKeyCollection = aciPreKeyCollection,
|
||||
|
||||
+4
-4
@@ -2041,7 +2041,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
try {
|
||||
SendMessageResponse response = NetworkResultUtil.toMessageSendLegacy(messages.getDestination(), messageApi.sendMessage(messages, sealedSenderAccess, story));
|
||||
return SendMessageResult.success(recipient, messages.getDevices(), response.sentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
|
||||
return SendMessageResult.success(recipient, messages.getDevices(), response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
|
||||
} catch (AuthorizationFailedException |
|
||||
UnregisteredUserException |
|
||||
MismatchedDevicesException |
|
||||
@@ -2080,7 +2080,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
SendMessageResponse response = socket.sendMessage(messages, sealedSenderAccess, story);
|
||||
|
||||
return SendMessageResult.success(recipient, messages.getDevices(), response.sentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
|
||||
return SendMessageResult.success(recipient, messages.getDevices(), response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
|
||||
|
||||
} catch (InvalidKeyException ike) {
|
||||
Log.w(TAG, ike);
|
||||
@@ -2302,7 +2302,7 @@ public class SignalServiceMessageSender {
|
||||
SendMessageResult result = SendMessageResult.success(
|
||||
recipient,
|
||||
messages.getDevices(),
|
||||
response.sentUnidentified(),
|
||||
response.getSentUnidentified(),
|
||||
response.getNeedsSync() || aciStore.isMultiDevice(),
|
||||
System.currentTimeMillis() - startTime,
|
||||
content.getContent()
|
||||
@@ -2345,7 +2345,7 @@ public class SignalServiceMessageSender {
|
||||
return SendMessageResult.success(
|
||||
recipient,
|
||||
messages.getDevices(),
|
||||
response.sentUnidentified(),
|
||||
response.getSentUnidentified(),
|
||||
response.getNeedsSync() || aciStore.isMultiDevice(),
|
||||
System.currentTimeMillis() - startTime,
|
||||
content.getContent()
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.account;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
|
||||
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity;
|
||||
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
|
||||
import org.signal.network.util.JsonUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ChangePhoneNumberRequest {
|
||||
@JsonProperty
|
||||
private String sessionId;
|
||||
|
||||
@JsonProperty
|
||||
private String recoveryPassword;
|
||||
|
||||
@JsonProperty
|
||||
private String number;
|
||||
|
||||
@JsonProperty("reglock")
|
||||
private String registrationLock;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = JsonUtil.IdentityKeySerializer.class)
|
||||
@JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class)
|
||||
private IdentityKey pniIdentityKey;
|
||||
|
||||
@JsonProperty
|
||||
private List<OutgoingPushMessage> deviceMessages;
|
||||
|
||||
@JsonProperty
|
||||
private Map<String, SignedPreKeyEntity> devicePniSignedPrekeys;
|
||||
|
||||
@JsonProperty("devicePniPqLastResortPrekeys")
|
||||
private Map<String, KyberPreKeyEntity> devicePniLastResortKyberPrekeys;
|
||||
|
||||
@JsonProperty
|
||||
private Map<String, Integer> pniRegistrationIds;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ChangePhoneNumberRequest() {}
|
||||
|
||||
public ChangePhoneNumberRequest(String sessionId,
|
||||
String recoveryPassword,
|
||||
String number,
|
||||
String registrationLock,
|
||||
IdentityKey pniIdentityKey,
|
||||
List<OutgoingPushMessage> deviceMessages,
|
||||
Map<String, SignedPreKeyEntity> devicePniSignedPrekeys,
|
||||
Map<String, KyberPreKeyEntity> devicePniLastResortKyberPrekeys,
|
||||
Map<String, Integer> pniRegistrationIds)
|
||||
{
|
||||
this.sessionId = sessionId;
|
||||
this.recoveryPassword = recoveryPassword;
|
||||
this.number = number;
|
||||
this.registrationLock = registrationLock;
|
||||
this.pniIdentityKey = pniIdentityKey;
|
||||
this.deviceMessages = deviceMessages;
|
||||
this.devicePniSignedPrekeys = devicePniSignedPrekeys;
|
||||
this.devicePniLastResortKyberPrekeys = devicePniLastResortKyberPrekeys;
|
||||
this.pniRegistrationIds = pniRegistrationIds;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public String getRegistrationLock() {
|
||||
return registrationLock;
|
||||
}
|
||||
|
||||
public IdentityKey getPniIdentityKey() {
|
||||
return pniIdentityKey;
|
||||
}
|
||||
|
||||
public List<OutgoingPushMessage> getDeviceMessages() {
|
||||
return deviceMessages;
|
||||
}
|
||||
|
||||
public Map<String, SignedPreKeyEntity> getDevicePniSignedPrekeys() {
|
||||
return devicePniSignedPrekeys;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getPniRegistrationIds() {
|
||||
return pniRegistrationIds;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.account
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.network.util.JsonUtil
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
|
||||
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity
|
||||
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
|
||||
|
||||
class ChangePhoneNumberRequest(
|
||||
val sessionId: String? = null,
|
||||
val recoveryPassword: String? = null,
|
||||
val number: String,
|
||||
@JsonProperty("reglock") val registrationLock: String? = null,
|
||||
@JsonSerialize(using = JsonUtil.IdentityKeySerializer::class)
|
||||
@JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer::class)
|
||||
val pniIdentityKey: IdentityKey,
|
||||
val deviceMessages: List<OutgoingPushMessage>,
|
||||
val devicePniSignedPrekeys: Map<String, SignedPreKeyEntity>,
|
||||
@JsonProperty("devicePniPqLastResortPrekeys") val devicePniLastResortKyberPrekeys: Map<String, KyberPreKeyEntity>,
|
||||
val pniRegistrationIds: Map<String, Int>
|
||||
)
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.whispersystems.signalservice.internal.push.DonationProcessor;
|
||||
|
||||
class BoostReceiptCredentialRequestJson {
|
||||
@JsonProperty("paymentIntentId")
|
||||
private final String paymentIntentId;
|
||||
|
||||
@JsonProperty("receiptCredentialRequest")
|
||||
private final String receiptCredentialRequest;
|
||||
|
||||
@JsonProperty("processor")
|
||||
private final String processor;
|
||||
|
||||
BoostReceiptCredentialRequestJson(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest, DonationProcessor processor) {
|
||||
this.paymentIntentId = paymentIntentId;
|
||||
this.receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize());
|
||||
this.processor = processor.getCode();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest
|
||||
import org.whispersystems.signalservice.internal.push.DonationProcessor
|
||||
|
||||
internal class BoostReceiptCredentialRequestJson(
|
||||
val paymentIntentId: String,
|
||||
val receiptCredentialRequest: String,
|
||||
val processor: String
|
||||
) {
|
||||
constructor(paymentIntentId: String, receiptCredentialRequest: ReceiptCredentialRequest, processor: DonationProcessor) : this(
|
||||
paymentIntentId = paymentIntentId,
|
||||
receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize()),
|
||||
processor = processor.code
|
||||
)
|
||||
}
|
||||
+2
-2
@@ -236,7 +236,7 @@ class DonationsApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSo
|
||||
val body = ReceiptCredentialRequestJson(receiptCredentialRequest)
|
||||
val request = WebSocketRequestMessage.post("/v1/subscription/${subscriberId.serialize()}/receipt_credentials", body)
|
||||
return NetworkResult.fromWebSocketRequest(unauthWebSocket, request, webSocketResponseConverter = NetworkResult.LongPollingWebSocketConverter(ReceiptCredentialResponseJson::class))
|
||||
.map { it.receiptCredentialResponse }
|
||||
.map { it.credentialResponse }
|
||||
.then {
|
||||
if (it != null) {
|
||||
NetworkResult.Success(it)
|
||||
@@ -260,7 +260,7 @@ class DonationsApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSo
|
||||
val body = BoostReceiptCredentialRequestJson(paymentIntentId, receiptCredentialRequest, processor)
|
||||
val request = WebSocketRequestMessage.post("/v1/subscription/boost/receipt_credentials", body)
|
||||
return NetworkResult.fromWebSocketRequest(unauthWebSocket, request, webSocketResponseConverter = NetworkResult.LongPollingWebSocketConverter(ReceiptCredentialResponseJson::class))
|
||||
.map { it.receiptCredentialResponse }
|
||||
.map { it.credentialResponse }
|
||||
.then {
|
||||
if (it != null) {
|
||||
NetworkResult.Success(it)
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Request JSON for confirming a PayPal one-time payment intent
|
||||
*/
|
||||
class PayPalConfirmOneTimePaymentIntentPayload {
|
||||
@JsonProperty
|
||||
private String amount;
|
||||
|
||||
@JsonProperty
|
||||
private String currency;
|
||||
|
||||
@JsonProperty
|
||||
private long level;
|
||||
|
||||
@JsonProperty
|
||||
private String payerId;
|
||||
|
||||
@JsonProperty
|
||||
private String paymentId;
|
||||
|
||||
@JsonProperty
|
||||
private String paymentToken;
|
||||
|
||||
public PayPalConfirmOneTimePaymentIntentPayload(String amount, String currency, long level, String payerId, String paymentId, String paymentToken) {
|
||||
this.amount = amount;
|
||||
this.currency = currency;
|
||||
this.level = level;
|
||||
this.payerId = payerId;
|
||||
this.paymentId = paymentId;
|
||||
this.paymentToken = paymentToken;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
/**
|
||||
* Request JSON for confirming a PayPal one-time payment intent
|
||||
*/
|
||||
internal class PayPalConfirmOneTimePaymentIntentPayload(
|
||||
val amount: String,
|
||||
val currency: String,
|
||||
val level: Long,
|
||||
val payerId: String,
|
||||
val paymentId: String,
|
||||
val paymentToken: String
|
||||
)
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Request JSON for creating a PayPal one-time payment intent
|
||||
*/
|
||||
class PayPalCreateOneTimePaymentIntentPayload {
|
||||
@JsonProperty
|
||||
private long amount;
|
||||
|
||||
@JsonProperty
|
||||
private String currency;
|
||||
|
||||
@JsonProperty
|
||||
private long level;
|
||||
|
||||
@JsonProperty
|
||||
private String returnUrl;
|
||||
|
||||
@JsonProperty
|
||||
private String cancelUrl;
|
||||
|
||||
public PayPalCreateOneTimePaymentIntentPayload(long amount, String currency, long level, String returnUrl, String cancelUrl) {
|
||||
this.amount = amount;
|
||||
this.currency = currency;
|
||||
this.level = level;
|
||||
this.returnUrl = returnUrl;
|
||||
this.cancelUrl = cancelUrl;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
/**
|
||||
* Request JSON for creating a PayPal one-time payment intent
|
||||
*/
|
||||
internal class PayPalCreateOneTimePaymentIntentPayload(
|
||||
val amount: Long,
|
||||
val currency: String,
|
||||
val level: Long,
|
||||
val returnUrl: String,
|
||||
val cancelUrl: String
|
||||
)
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
class PayPalCreatePaymentMethodPayload {
|
||||
@JsonProperty
|
||||
private String returnUrl;
|
||||
|
||||
@JsonProperty
|
||||
private String cancelUrl;
|
||||
|
||||
PayPalCreatePaymentMethodPayload(String returnUrl, String cancelUrl) {
|
||||
this.returnUrl = returnUrl;
|
||||
this.cancelUrl = cancelUrl;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
/**
|
||||
* Request JSON for creating a recurring PayPal payment method
|
||||
*/
|
||||
internal class PayPalCreatePaymentMethodPayload(
|
||||
val returnUrl: String,
|
||||
val cancelUrl: String
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
|
||||
import org.signal.core.util.Base64;
|
||||
|
||||
class ReceiptCredentialRequestJson {
|
||||
@JsonProperty("receiptCredentialRequest")
|
||||
private final String receiptCredentialRequest;
|
||||
|
||||
ReceiptCredentialRequestJson(ReceiptCredentialRequest receiptCredentialRequest) {
|
||||
this.receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize());
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest
|
||||
|
||||
internal class ReceiptCredentialRequestJson(
|
||||
val receiptCredentialRequest: String
|
||||
) {
|
||||
constructor(receiptCredentialRequest: ReceiptCredentialRequest) : this(Base64.encodeWithPadding(receiptCredentialRequest.serialize()))
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialResponse;
|
||||
import org.signal.core.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class ReceiptCredentialResponseJson {
|
||||
|
||||
private final ReceiptCredentialResponse receiptCredentialResponse;
|
||||
|
||||
ReceiptCredentialResponseJson(@JsonProperty("receiptCredentialResponse") String receiptCredentialResponse) {
|
||||
ReceiptCredentialResponse response;
|
||||
try {
|
||||
response = new ReceiptCredentialResponse(Base64.decode(receiptCredentialResponse));
|
||||
} catch (IOException | InvalidInputException e) {
|
||||
response = null;
|
||||
}
|
||||
|
||||
this.receiptCredentialResponse = response;
|
||||
}
|
||||
|
||||
public @Nullable ReceiptCredentialResponse getReceiptCredentialResponse() {
|
||||
return receiptCredentialResponse;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialResponse
|
||||
|
||||
internal class ReceiptCredentialResponseJson(
|
||||
@JsonProperty("receiptCredentialResponse") receiptCredentialResponse: String
|
||||
) {
|
||||
/** Null if the server sent something that isn't a serialized [ReceiptCredentialResponse]. */
|
||||
val credentialResponse: ReceiptCredentialResponse? = runCatching { ReceiptCredentialResponse(Base64.decode(receiptCredentialResponse)) }.getOrNull()
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
|
||||
|
||||
/**
|
||||
* POST /v1/donation/redeem-receipt
|
||||
*
|
||||
* Request object for redeeming a receipt from a donation transaction.
|
||||
*/
|
||||
class RedeemDonationReceiptRequest {
|
||||
|
||||
private final String receiptCredentialPresentation;
|
||||
private final boolean visible;
|
||||
private final boolean primary;
|
||||
|
||||
/**
|
||||
* @param receiptCredentialPresentation base64-encoded no-newlines standard-character-set with-padding of the bytes of a {@link ReceiptCredentialPresentation} object
|
||||
* @param visible boolean indicating if the new badge should be visible or not on the profile
|
||||
* @param primary boolean indicating if the new badge should be primary or not on the profile; is always treated as false if `visible` is false
|
||||
*/
|
||||
@JsonCreator RedeemDonationReceiptRequest(
|
||||
@JsonProperty("receiptCredentialPresentation") String receiptCredentialPresentation,
|
||||
@JsonProperty("visible") boolean visible,
|
||||
@JsonProperty("primary") boolean primary) {
|
||||
|
||||
this.receiptCredentialPresentation = receiptCredentialPresentation;
|
||||
this.visible = visible;
|
||||
this.primary = primary;
|
||||
}
|
||||
|
||||
public String getReceiptCredentialPresentation() {
|
||||
return receiptCredentialPresentation;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public boolean isPrimary() {
|
||||
return primary;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
/**
|
||||
* POST /v1/donation/redeem-receipt
|
||||
*
|
||||
* Request object for redeeming a receipt from a donation transaction.
|
||||
*
|
||||
* @param receiptCredentialPresentation base64-encoded no-newlines standard-character-set with-padding of the bytes of a `ReceiptCredentialPresentation`
|
||||
* @param visible Whether the new badge should be visible on the profile
|
||||
* @param primary Whether the new badge should be primary on the profile; always treated as false if [visible] is false
|
||||
*/
|
||||
internal class RedeemDonationReceiptRequest(
|
||||
val receiptCredentialPresentation: String,
|
||||
val visible: Boolean,
|
||||
val primary: Boolean
|
||||
)
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
class StripeOneTimePaymentIntentPayload {
|
||||
@JsonProperty
|
||||
private long amount;
|
||||
|
||||
@JsonProperty
|
||||
private String currency;
|
||||
|
||||
@JsonProperty
|
||||
private long level;
|
||||
|
||||
@JsonProperty
|
||||
private String paymentMethod;
|
||||
|
||||
public StripeOneTimePaymentIntentPayload(long amount, String currency, long level, String paymentMethod) {
|
||||
this.amount = amount;
|
||||
this.currency = currency;
|
||||
this.level = level;
|
||||
this.paymentMethod = paymentMethod;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.donations
|
||||
|
||||
/**
|
||||
* Request JSON for creating a Stripe one-time payment intent
|
||||
*/
|
||||
internal class StripeOneTimePaymentIntentPayload(
|
||||
val amount: Long,
|
||||
val currency: String,
|
||||
val level: Long,
|
||||
val paymentMethod: String
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.groupsv2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CredentialResponse {
|
||||
|
||||
@JsonProperty
|
||||
private TemporalCredential[] credentials;
|
||||
|
||||
@JsonProperty
|
||||
private TemporalCredential[] callLinkAuthCredentials;
|
||||
|
||||
public TemporalCredential[] getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public TemporalCredential[] getCallLinkAuthCredentials() {
|
||||
return callLinkAuthCredentials;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.groupsv2
|
||||
|
||||
class CredentialResponse(
|
||||
val credentials: Array<TemporalCredential> = emptyArray(),
|
||||
val callLinkAuthCredentials: Array<TemporalCredential> = emptyArray()
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.groupsv2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class TemporalCredential {
|
||||
|
||||
@JsonProperty
|
||||
private byte[] credential;
|
||||
|
||||
@JsonProperty
|
||||
private long redemptionTime;
|
||||
|
||||
public byte[] getCredential() {
|
||||
return credential;
|
||||
}
|
||||
|
||||
public long getRedemptionTime() {
|
||||
return redemptionTime;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.groupsv2
|
||||
|
||||
class TemporalCredential(
|
||||
val credential: ByteArray,
|
||||
val redemptionTime: Long
|
||||
)
|
||||
+18
-15
@@ -250,7 +250,7 @@ class KeysApi(
|
||||
return this.map { response ->
|
||||
val bundles: MutableList<PreKeyBundle> = LinkedList()
|
||||
|
||||
for (device in response.getDevices()) {
|
||||
for (device in response.devices) {
|
||||
var preKey: ECPublicKey? = null
|
||||
var signedPreKey: ECPublicKey?
|
||||
var signedPreKeySignature: ByteArray?
|
||||
@@ -260,39 +260,42 @@ class KeysApi(
|
||||
var kyberPreKey: KEMPublicKey?
|
||||
var kyberPreKeySignature: ByteArray?
|
||||
|
||||
if (device.getSignedPreKey() != null) {
|
||||
val rawSignedPreKeyId = device.getSignedPreKey().keyId
|
||||
val deviceSignedPreKey = device.signedPreKey
|
||||
if (deviceSignedPreKey != null) {
|
||||
val rawSignedPreKeyId = deviceSignedPreKey.keyId
|
||||
if (rawSignedPreKeyId !in 0..Int.MAX_VALUE.toLong()) {
|
||||
Log.w(TAG, "Signed pre-key ID for device ${device.deviceId} is out of valid range! Skipping.")
|
||||
continue
|
||||
}
|
||||
signedPreKey = device.getSignedPreKey().publicKey
|
||||
signedPreKey = deviceSignedPreKey.publicKey
|
||||
signedPreKeyId = rawSignedPreKeyId.toInt()
|
||||
signedPreKeySignature = device.getSignedPreKey().signature
|
||||
signedPreKeySignature = deviceSignedPreKey.signature
|
||||
} else {
|
||||
Log.w(TAG, "No signed prekey for device ${device.deviceId}! Skipping.")
|
||||
continue
|
||||
}
|
||||
|
||||
if (device.getPreKey() != null) {
|
||||
val rawPreKeyId = device.getPreKey().keyId
|
||||
val devicePreKey = device.preKey
|
||||
if (devicePreKey != null) {
|
||||
val rawPreKeyId = devicePreKey.keyId
|
||||
if (rawPreKeyId !in 0..Int.MAX_VALUE.toLong()) {
|
||||
Log.w(TAG, "Pre-key ID for device ${device.deviceId} is out of valid range! Skipping.")
|
||||
continue
|
||||
}
|
||||
preKeyId = rawPreKeyId.toInt()
|
||||
preKey = device.getPreKey().publicKey
|
||||
preKey = devicePreKey.publicKey
|
||||
}
|
||||
|
||||
if (device.getKyberPreKey() != null) {
|
||||
val rawKyberPreKeyId = device.getKyberPreKey().keyId
|
||||
val deviceKyberPreKey = device.kyberPreKey
|
||||
if (deviceKyberPreKey != null) {
|
||||
val rawKyberPreKeyId = deviceKyberPreKey.keyId
|
||||
if (rawKyberPreKeyId !in 0..Int.MAX_VALUE.toLong()) {
|
||||
Log.w(TAG, "Kyber pre-key ID for device ${device.deviceId} is out of valid range! Skipping.")
|
||||
continue
|
||||
}
|
||||
kyberPreKey = device.getKyberPreKey().publicKey
|
||||
kyberPreKey = deviceKyberPreKey.publicKey
|
||||
kyberPreKeyId = rawKyberPreKeyId.toInt()
|
||||
kyberPreKeySignature = device.getKyberPreKey().signature
|
||||
kyberPreKeySignature = deviceKyberPreKey.signature
|
||||
} else {
|
||||
Log.w(TAG, "No kyber prekey for device ${device.deviceId}! Skipping.")
|
||||
continue
|
||||
@@ -300,14 +303,14 @@ class KeysApi(
|
||||
|
||||
bundles.add(
|
||||
PreKeyBundle(
|
||||
device.getRegistrationId(),
|
||||
device.getDeviceId(),
|
||||
device.registrationId,
|
||||
device.deviceId,
|
||||
preKeyId,
|
||||
preKey,
|
||||
signedPreKeyId,
|
||||
signedPreKey,
|
||||
signedPreKeySignature,
|
||||
response.getIdentityKey(),
|
||||
response.identityKey,
|
||||
kyberPreKeyId,
|
||||
kyberPreKey,
|
||||
kyberPreKeySignature
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.keys;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class OneTimePreKeyCounts {
|
||||
|
||||
@JsonProperty("count")
|
||||
private int ecCount;
|
||||
|
||||
@JsonProperty("pqCount")
|
||||
private int kyberCount;
|
||||
|
||||
public OneTimePreKeyCounts() {}
|
||||
|
||||
public OneTimePreKeyCounts(int ecCount, int kyberCount) {
|
||||
this.ecCount = ecCount;
|
||||
this.kyberCount = kyberCount;
|
||||
}
|
||||
|
||||
public int getEcCount() {
|
||||
return ecCount;
|
||||
}
|
||||
|
||||
public int getKyberCount() {
|
||||
return kyberCount;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.keys
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
data class OneTimePreKeyCounts(
|
||||
@JsonProperty("count") val ecCount: Int = 0,
|
||||
@JsonProperty("pqCount") val kyberCount: Int = 0
|
||||
)
|
||||
+2
-2
@@ -33,14 +33,14 @@ class MessageApi(
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Adjust the default parsing of [SendMessageResponse] to set the non-server returned [SendMessageResponse.sentUnidentfied]
|
||||
* Adjust the default parsing of [SendMessageResponse] to set the non-server returned [SendMessageResponse.sentUnidentified]
|
||||
* flag on the model.
|
||||
*/
|
||||
private val sendMessageResponseConverter = object : NetworkResult.WebSocketResponseConverter<SendMessageResponse> {
|
||||
override fun convert(response: WebsocketResponse): NetworkResult<SendMessageResponse> {
|
||||
return if (response.status == 200) {
|
||||
response.toSuccess(SendMessageResponse::class)
|
||||
.map { it.apply { setSentUnidentfied(response.isUnidentified) } }
|
||||
.map { it.apply { sentUnidentified = response.isUnidentified } }
|
||||
} else {
|
||||
response.toStatusCodeError()
|
||||
}
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.messages.calls;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TurnServerInfo {
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
@JsonProperty
|
||||
private String password;
|
||||
|
||||
@JsonProperty
|
||||
private String hostname;
|
||||
|
||||
@JsonProperty
|
||||
private List<String> urls;
|
||||
|
||||
@JsonProperty
|
||||
private List<String> urlsWithIps;
|
||||
|
||||
@JsonProperty
|
||||
private Long ttl;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
// Hostname for the ips in urlsWithIps
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public List<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
public List<String> getUrlsWithIps() {
|
||||
return urlsWithIps;
|
||||
}
|
||||
|
||||
public Long getTtl() {
|
||||
return ttl;
|
||||
};
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.messages.calls
|
||||
|
||||
data class TurnServerInfo(
|
||||
val username: String? = null,
|
||||
val password: String? = null,
|
||||
/** Hostname for the ips in [urlsWithIps]. */
|
||||
val hostname: String? = null,
|
||||
val urls: List<String>? = null,
|
||||
val urlsWithIps: List<String>? = null,
|
||||
val ttl: Long? = null
|
||||
)
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
||||
*
|
||||
* Licensed according to the LICENSE file in this repository.
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.messages.multidevice;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DeviceInfo {
|
||||
|
||||
@JsonProperty
|
||||
public int id;
|
||||
|
||||
@JsonProperty
|
||||
public String name;
|
||||
|
||||
@JsonProperty
|
||||
public long lastSeen;
|
||||
|
||||
@JsonProperty
|
||||
public int registrationId;
|
||||
|
||||
@JsonProperty
|
||||
public String createdAtCiphertext;
|
||||
|
||||
public DeviceInfo() {}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public int getRegistrationId() {
|
||||
return registrationId;
|
||||
}
|
||||
|
||||
public String getCreatedAtCiphertext() {
|
||||
return createdAtCiphertext;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.messages.multidevice
|
||||
|
||||
data class DeviceInfo(
|
||||
val id: Int = 0,
|
||||
val name: String? = null,
|
||||
val lastSeen: Long = 0,
|
||||
val registrationId: Int = 0,
|
||||
val createdAtCiphertext: String? = null
|
||||
)
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.messages.multidevice;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class RegisterAsSecondaryDeviceResponse {
|
||||
@JsonProperty
|
||||
private UUID uuid;
|
||||
|
||||
@JsonProperty
|
||||
private UUID pni;
|
||||
|
||||
@JsonProperty
|
||||
private String deviceId;
|
||||
|
||||
public RegisterAsSecondaryDeviceResponse() {}
|
||||
|
||||
public RegisterAsSecondaryDeviceResponse(UUID uuid, UUID pni, String deviceId) {
|
||||
this.uuid = uuid;
|
||||
this.pni = pni;
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public UUID getPni() {
|
||||
return pni;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.messages.multidevice
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
class RegisterAsSecondaryDeviceResponse(
|
||||
val uuid: UUID,
|
||||
val pni: UUID,
|
||||
val deviceId: String
|
||||
)
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.payments;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class CurrencyConversion {
|
||||
@JsonProperty
|
||||
private String base;
|
||||
|
||||
@JsonProperty
|
||||
private Map<String, Double> conversions;
|
||||
|
||||
public String getBase() {
|
||||
return base;
|
||||
}
|
||||
|
||||
public Map<String, Double> getConversions() {
|
||||
return conversions;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.payments
|
||||
|
||||
data class CurrencyConversion(
|
||||
val base: String = "",
|
||||
val conversions: Map<String, Double> = emptyMap()
|
||||
)
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.payments;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class CurrencyConversions {
|
||||
@JsonProperty
|
||||
private List<CurrencyConversion> currencies;
|
||||
|
||||
@JsonProperty
|
||||
private long timestamp;
|
||||
|
||||
public List<CurrencyConversion> getCurrencies() {
|
||||
return currencies;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.payments
|
||||
|
||||
data class CurrencyConversions(
|
||||
val currencies: List<CurrencyConversion> = emptyList(),
|
||||
val timestamp: Long = 0
|
||||
)
|
||||
-241
@@ -1,241 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.profiles;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.core.models.ServiceId;
|
||||
import org.signal.network.util.JsonUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class SignalServiceProfile {
|
||||
|
||||
public enum RequestType {
|
||||
PROFILE,
|
||||
PROFILE_AND_CREDENTIAL
|
||||
}
|
||||
|
||||
private static final String TAG = SignalServiceProfile.class.getSimpleName();
|
||||
|
||||
@JsonProperty
|
||||
private String identityKey;
|
||||
|
||||
@JsonProperty
|
||||
private String name;
|
||||
|
||||
@JsonProperty
|
||||
private String about;
|
||||
|
||||
@JsonProperty
|
||||
private String aboutEmoji;
|
||||
|
||||
@JsonProperty
|
||||
private byte[] paymentAddress;
|
||||
|
||||
@JsonProperty
|
||||
private String avatar;
|
||||
|
||||
@JsonProperty
|
||||
private String unidentifiedAccess;
|
||||
|
||||
@JsonProperty
|
||||
private boolean unrestrictedUnidentifiedAccess;
|
||||
|
||||
@JsonProperty
|
||||
private Capabilities capabilities;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = JsonUtil.ServiceIdSerializer.class)
|
||||
@JsonDeserialize(using = JsonUtil.ServiceIdDeserializer.class)
|
||||
private ServiceId uuid;
|
||||
|
||||
@JsonProperty
|
||||
private byte[] credential;
|
||||
|
||||
@JsonProperty
|
||||
private List<Badge> badges;
|
||||
|
||||
@JsonProperty
|
||||
private String phoneNumberSharing;
|
||||
|
||||
@JsonIgnore
|
||||
private RequestType requestType;
|
||||
|
||||
public SignalServiceProfile() {}
|
||||
|
||||
public String getIdentityKey() {
|
||||
return identityKey;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getAbout() {
|
||||
return about;
|
||||
}
|
||||
|
||||
public String getAboutEmoji() {
|
||||
return aboutEmoji;
|
||||
}
|
||||
|
||||
public byte[] getPaymentAddress() {
|
||||
return paymentAddress;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public String getUnidentifiedAccess() {
|
||||
return unidentifiedAccess;
|
||||
}
|
||||
|
||||
public String getPhoneNumberSharing() {
|
||||
return phoneNumberSharing;
|
||||
}
|
||||
|
||||
public boolean isUnrestrictedUnidentifiedAccess() {
|
||||
return unrestrictedUnidentifiedAccess;
|
||||
}
|
||||
|
||||
public Capabilities getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public List<Badge> getBadges() {
|
||||
return badges;
|
||||
}
|
||||
|
||||
public ServiceId getServiceId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public RequestType getRequestType() {
|
||||
return requestType;
|
||||
}
|
||||
|
||||
public void setRequestType(RequestType requestType) {
|
||||
this.requestType = requestType;
|
||||
}
|
||||
|
||||
public static class Badge {
|
||||
@JsonProperty
|
||||
private String id;
|
||||
|
||||
@JsonProperty
|
||||
private String category;
|
||||
|
||||
@JsonProperty
|
||||
private String name;
|
||||
|
||||
@JsonProperty
|
||||
private String description;
|
||||
|
||||
@JsonProperty
|
||||
private List<String> sprites6;
|
||||
|
||||
@JsonProperty
|
||||
private BigDecimal expiration;
|
||||
|
||||
@JsonProperty
|
||||
private boolean visible;
|
||||
|
||||
@JsonProperty
|
||||
private long duration;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<String> getSprites6() {
|
||||
return sprites6;
|
||||
}
|
||||
|
||||
public BigDecimal getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Duration badge is valid for, in seconds.
|
||||
*/
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Capabilities {
|
||||
@JsonProperty
|
||||
private boolean storage;
|
||||
|
||||
@JsonProperty("ssre2")
|
||||
private boolean storageServiceEncryptionV2;
|
||||
|
||||
@JsonProperty("usernameChangeSyncMessage")
|
||||
private boolean usernameSyncMessages;
|
||||
|
||||
@JsonProperty
|
||||
private boolean optionalPhoneNumber;
|
||||
|
||||
@JsonCreator
|
||||
public Capabilities() {}
|
||||
|
||||
public Capabilities(boolean storage, boolean storageServiceEncryptionV2, boolean usernameSyncMessages, boolean optionalPhoneNumber) {
|
||||
this.storage = storage;
|
||||
this.storageServiceEncryptionV2 = storageServiceEncryptionV2;
|
||||
this.usernameSyncMessages = usernameSyncMessages;
|
||||
this.optionalPhoneNumber = optionalPhoneNumber;
|
||||
}
|
||||
|
||||
public boolean isStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public boolean isStorageServiceEncryptionV2() {
|
||||
return storageServiceEncryptionV2;
|
||||
}
|
||||
|
||||
public boolean isUsernameSyncMessages() {
|
||||
return usernameSyncMessages;
|
||||
}
|
||||
|
||||
public boolean isOptionalPhoneNumber() {
|
||||
return optionalPhoneNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {
|
||||
if (credential == null) return null;
|
||||
|
||||
try {
|
||||
return new ExpiringProfileKeyCredentialResponse(credential);
|
||||
} catch (InvalidInputException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.profiles
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse
|
||||
import org.signal.network.util.JsonUtil
|
||||
import java.math.BigDecimal
|
||||
|
||||
class SignalServiceProfile(
|
||||
val identityKey: String? = null,
|
||||
val name: String? = null,
|
||||
val about: String? = null,
|
||||
val aboutEmoji: String? = null,
|
||||
val paymentAddress: ByteArray? = null,
|
||||
val avatar: String? = null,
|
||||
val unidentifiedAccess: String? = null,
|
||||
val unrestrictedUnidentifiedAccess: Boolean = false,
|
||||
val capabilities: Capabilities? = null,
|
||||
@JsonProperty("uuid")
|
||||
@JsonSerialize(using = JsonUtil.ServiceIdSerializer::class)
|
||||
@JsonDeserialize(using = JsonUtil.ServiceIdDeserializer::class)
|
||||
val serviceId: ServiceId? = null,
|
||||
val credential: ByteArray? = null,
|
||||
val badges: List<Badge>? = null,
|
||||
val phoneNumberSharing: String? = null
|
||||
) {
|
||||
|
||||
val expiringProfileKeyCredentialResponse: ExpiringProfileKeyCredentialResponse?
|
||||
get() {
|
||||
if (credential == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return try {
|
||||
ExpiringProfileKeyCredentialResponse(credential)
|
||||
} catch (e: InvalidInputException) {
|
||||
Log.w(TAG, e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
enum class RequestType {
|
||||
PROFILE,
|
||||
PROFILE_AND_CREDENTIAL
|
||||
}
|
||||
|
||||
data class Badge(
|
||||
val id: String = "",
|
||||
val category: String = "",
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val sprites6: List<String> = emptyList(),
|
||||
val expiration: BigDecimal? = null,
|
||||
val visible: Boolean = false,
|
||||
/** Duration the badge is valid for, in seconds. */
|
||||
val duration: Long = 0
|
||||
)
|
||||
|
||||
data class Capabilities(
|
||||
val storage: Boolean = false,
|
||||
@JsonProperty("ssre2") val storageServiceEncryptionV2: Boolean = false,
|
||||
@JsonProperty("usernameChangeSyncMessage") val usernameSyncMessages: Boolean = false,
|
||||
val optionalPhoneNumber: Boolean = false
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(SignalServiceProfile::class)
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.provisioning;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class ProvisioningMessage {
|
||||
|
||||
@JsonProperty
|
||||
private String body;
|
||||
|
||||
public ProvisioningMessage(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.provisioning
|
||||
|
||||
class ProvisioningMessage(
|
||||
val body: String
|
||||
)
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
||||
*
|
||||
* Licensed according to the LICENSE file in this repository.
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey;
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyEntity;
|
||||
import org.signal.core.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SignedPreKeyEntity extends PreKeyEntity {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArraySerializer.class)
|
||||
@JsonDeserialize(using = ByteArrayDeserializer.class)
|
||||
private byte[] signature;
|
||||
|
||||
public SignedPreKeyEntity() {}
|
||||
|
||||
public SignedPreKeyEntity(long keyId, ECPublicKey publicKey, byte[] signature) {
|
||||
super(keyId, publicKey);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
private static class ByteArraySerializer extends JsonSerializer<byte[]> {
|
||||
@Override
|
||||
public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(Base64.encodeWithoutPadding(value));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ByteArrayDeserializer extends JsonDeserializer<byte[]> {
|
||||
|
||||
@Override
|
||||
public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return Base64.decode(p.getValueAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.push
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.whispersystems.signalservice.internal.push.ByteArrayDeserializerBase64
|
||||
import org.whispersystems.signalservice.internal.push.ByteArraySerializerBase64NoPadding
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyEntity
|
||||
|
||||
class SignedPreKeyEntity(
|
||||
val keyId: Long,
|
||||
@JsonSerialize(using = PreKeyEntity.ECPublicKeySerializer::class)
|
||||
@JsonDeserialize(using = PreKeyEntity.ECPublicKeyDeserializer::class)
|
||||
val publicKey: ECPublicKey,
|
||||
@JsonSerialize(using = ByteArraySerializerBase64NoPadding::class)
|
||||
@JsonDeserialize(using = ByteArrayDeserializerBase64::class)
|
||||
val signature: ByteArray
|
||||
)
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.push.exceptions;
|
||||
|
||||
import org.signal.network.exceptions.MalformedResponseException;
|
||||
|
||||
import org.signal.network.exceptions.NonSuccessfulResponseCodeException;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.signal.network.util.JsonUtil;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Response indicating we gave the server a non-normalized phone number. The expected normalized version of the number is provided.
|
||||
*/
|
||||
public class NonNormalizedPhoneNumberException extends NonSuccessfulResponseCodeException {
|
||||
|
||||
private final String originalNumber;
|
||||
private final String normalizedNumber;
|
||||
|
||||
public static NonNormalizedPhoneNumberException forResponse(@NonNull String responseBody) throws MalformedResponseException {
|
||||
JsonResponse response = JsonUtil.fromJsonResponse(responseBody, JsonResponse.class);
|
||||
return new NonNormalizedPhoneNumberException(response.originalNumber, response.normalizedNumber);
|
||||
}
|
||||
|
||||
public NonNormalizedPhoneNumberException(String originalNumber, String normalizedNumber) {
|
||||
super(400);
|
||||
|
||||
this.originalNumber = originalNumber;
|
||||
this.normalizedNumber = normalizedNumber;
|
||||
}
|
||||
|
||||
public String getOriginalNumber() {
|
||||
return originalNumber;
|
||||
}
|
||||
|
||||
public String getNormalizedNumber() {
|
||||
return normalizedNumber;
|
||||
}
|
||||
|
||||
private static class JsonResponse {
|
||||
@JsonProperty
|
||||
private String originalNumber;
|
||||
|
||||
@JsonProperty
|
||||
private String normalizedNumber;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.push.exceptions
|
||||
|
||||
import org.signal.network.exceptions.MalformedResponseException
|
||||
import org.signal.network.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.signal.network.util.JsonUtil
|
||||
|
||||
/**
|
||||
* Response indicating we gave the server a non-normalized phone number. The expected normalized version of the number is provided.
|
||||
*/
|
||||
class NonNormalizedPhoneNumberException(
|
||||
val originalNumber: String,
|
||||
val normalizedNumber: String
|
||||
) : NonSuccessfulResponseCodeException(400) {
|
||||
|
||||
/** The 400 response body. Separate from the exception itself so that it can be deserialized. */
|
||||
class Body(
|
||||
val originalNumber: String? = null,
|
||||
val normalizedNumber: String? = null
|
||||
)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(MalformedResponseException::class)
|
||||
fun forResponse(responseBody: String): NonNormalizedPhoneNumberException {
|
||||
val body = JsonUtil.fromJsonResponse(responseBody, Body::class.java)
|
||||
|
||||
if (body.originalNumber == null || body.normalizedNumber == null) {
|
||||
throw MalformedResponseException("Response is missing a number")
|
||||
}
|
||||
|
||||
return NonNormalizedPhoneNumberException(body.originalNumber, body.normalizedNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.storage;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class StorageAuthResponse {
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
@JsonProperty
|
||||
private String password;
|
||||
|
||||
public StorageAuthResponse() { }
|
||||
|
||||
public StorageAuthResponse(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.storage
|
||||
|
||||
data class StorageAuthResponse(
|
||||
val username: String,
|
||||
val password: String
|
||||
)
|
||||
-399
@@ -1,399 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.subscriptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class ActiveSubscription {
|
||||
|
||||
public static final String PAYMENT_METHOD_SEPA_DEBIT = "SEPA_DEBIT";
|
||||
|
||||
public static final ActiveSubscription EMPTY = new ActiveSubscription(null, null);
|
||||
|
||||
public enum Processor {
|
||||
STRIPE("STRIPE"),
|
||||
BRAINTREE("BRAINTREE"),
|
||||
GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING");
|
||||
|
||||
private final String code;
|
||||
|
||||
Processor(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
static Processor fromCode(String code) {
|
||||
for (Processor value : Processor.values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return STRIPE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As per API documentation
|
||||
*/
|
||||
public enum PaymentMethod {
|
||||
UNKNOWN("UNKNOWN"),
|
||||
CARD("CARD"),
|
||||
PAYPAL("PAYPAL"),
|
||||
SEPA_DEBIT("SEPA_DEBIT"),
|
||||
IDEAL("IDEAL"),
|
||||
GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"),
|
||||
APPLE_APP_STORE("APPLE_APP_STORE");
|
||||
|
||||
private String code;
|
||||
|
||||
PaymentMethod(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
static PaymentMethod fromCode(String code) {
|
||||
for (PaymentMethod method : PaymentMethod.values()) {
|
||||
if (Objects.equals(method.code, code)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return PaymentMethod.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
/**
|
||||
* The subscription is currently in a trial period and it's safe to provision your product for your customer.
|
||||
* The subscription transitions automatically to active when the first payment is made.
|
||||
*/
|
||||
TRIALING("trialing"),
|
||||
|
||||
/**
|
||||
* The subscription is in good standing and the most recent payment was successful. It's safe to provision your product for your customer.
|
||||
*/
|
||||
ACTIVE("active"),
|
||||
|
||||
/**
|
||||
* Payment failed when you created the subscription. A successful payment needs to be made within 23 hours to activate the subscription.
|
||||
*/
|
||||
INCOMPLETE("incomplete"),
|
||||
|
||||
/**
|
||||
* The initial payment on the subscription failed and no successful payment was made within 23 hours of creating the subscription.
|
||||
* These subscriptions don't bill customers. This status exists so you can track customers that failed to activate their subscriptions.
|
||||
*/
|
||||
INCOMPLETE_EXPIRED("incomplete_expired"),
|
||||
|
||||
/**
|
||||
* Payment on the latest invoice either failed or wasn't attempted.
|
||||
*/
|
||||
PAST_DUE("past_due"),
|
||||
|
||||
/**
|
||||
* The subscription has been canceled. During cancellation, automatic collection for all unpaid invoices is disabled (auto_advance=false).
|
||||
*/
|
||||
CANCELED("canceled"),
|
||||
|
||||
/**
|
||||
* The latest invoice hasn't been paid but the subscription remains in place.
|
||||
* The latest invoice remains open and invoices continue to be generated but payments aren't attempted.
|
||||
*/
|
||||
UNPAID("unpaid");
|
||||
|
||||
private final String status;
|
||||
|
||||
private static final Set<Status> FAILURE_STATUSES = new HashSet<>(Arrays.asList(
|
||||
INCOMPLETE_EXPIRED,
|
||||
PAST_DUE,
|
||||
UNPAID
|
||||
));
|
||||
|
||||
Status(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static Status getStatus(String status) {
|
||||
for (Status s : Status.values()) {
|
||||
if (Objects.equals(status, s.status)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown status " + status);
|
||||
}
|
||||
|
||||
static boolean isPaymentFailed(String status) {
|
||||
return FAILURE_STATUSES.contains(getStatus(status));
|
||||
}
|
||||
}
|
||||
|
||||
private final Subscription activeSubscription;
|
||||
private final ChargeFailure chargeFailure;
|
||||
|
||||
@JsonCreator
|
||||
public ActiveSubscription(@JsonProperty("subscription") Subscription activeSubscription,
|
||||
@JsonProperty("chargeFailure") ChargeFailure chargeFailure)
|
||||
{
|
||||
this.activeSubscription = activeSubscription;
|
||||
this.chargeFailure = chargeFailure;
|
||||
}
|
||||
|
||||
public Subscription getActiveSubscription() {
|
||||
return activeSubscription;
|
||||
}
|
||||
|
||||
public ChargeFailure getChargeFailure() {
|
||||
return chargeFailure;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return activeSubscription != null && activeSubscription.isActive();
|
||||
}
|
||||
|
||||
public boolean isPendingBankTransfer() {
|
||||
return activeSubscription != null && Objects.equals(activeSubscription.paymentMethod, PAYMENT_METHOD_SEPA_DEBIT) && activeSubscription.paymentPending;
|
||||
}
|
||||
|
||||
public boolean isInProgress() {
|
||||
return activeSubscription != null && !isActive() && (!isFailedPayment() || isPastDue()) && !isCanceled();
|
||||
}
|
||||
|
||||
public boolean isPastDue() {
|
||||
return activeSubscription != null && activeSubscription.isPastDue();
|
||||
}
|
||||
|
||||
public boolean isFailedPayment() {
|
||||
return chargeFailure != null || (activeSubscription != null && !isActive() && activeSubscription.isFailedPayment());
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return activeSubscription != null && activeSubscription.isCanceled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Backups-specific call that gives us a value that should align with autoRenew from the GPB payment.
|
||||
*/
|
||||
public boolean willCancelAtPeriodEnd() {
|
||||
return activeSubscription == null || activeSubscription.willCancelAtPeriodEnd;
|
||||
}
|
||||
|
||||
public static final class Subscription {
|
||||
private final int level;
|
||||
private final String currency;
|
||||
private final BigDecimal amount;
|
||||
private final long endOfCurrentPeriod;
|
||||
private final boolean isActive;
|
||||
private final long billingCycleAnchor;
|
||||
private final boolean willCancelAtPeriodEnd;
|
||||
private final String status;
|
||||
private final Processor processor;
|
||||
private final PaymentMethod paymentMethod;
|
||||
private final boolean paymentPending;
|
||||
|
||||
@JsonCreator
|
||||
public Subscription(@JsonProperty("level") int level,
|
||||
@JsonProperty("currency") String currency,
|
||||
@JsonProperty("amount") BigDecimal amount,
|
||||
@JsonProperty("endOfCurrentPeriod") long endOfCurrentPeriod,
|
||||
@JsonProperty("active") boolean isActive,
|
||||
@JsonProperty("billingCycleAnchor") long billingCycleAnchor,
|
||||
@JsonProperty("cancelAtPeriodEnd") boolean willCancelAtPeriodEnd,
|
||||
@JsonProperty("status") String status,
|
||||
@JsonProperty("processor") String processor,
|
||||
@JsonProperty("paymentMethod") String paymentMethod,
|
||||
@JsonProperty("paymentPending") boolean paymentPending)
|
||||
{
|
||||
this.level = level;
|
||||
this.currency = currency;
|
||||
this.amount = amount;
|
||||
this.endOfCurrentPeriod = endOfCurrentPeriod;
|
||||
this.isActive = isActive;
|
||||
this.billingCycleAnchor = billingCycleAnchor;
|
||||
this.willCancelAtPeriodEnd = willCancelAtPeriodEnd;
|
||||
this.status = status;
|
||||
this.processor = Processor.fromCode(processor);
|
||||
this.paymentMethod = PaymentMethod.fromCode(paymentMethod);
|
||||
this.paymentPending = paymentPending;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public String getCurrency() {
|
||||
return currency;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* UNIX Epoch Timestamp in seconds, can be used to calculate next billing date per
|
||||
* https://stripe.com/docs/billing/subscriptions/billing-cycle
|
||||
*/
|
||||
public long getBillingCycleAnchor() {
|
||||
return billingCycleAnchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this subscription is currently active.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* UNIX Epoch Timestamp in seconds
|
||||
*/
|
||||
public long getEndOfCurrentPeriod() {
|
||||
return endOfCurrentPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this subscription is set to end at the end of the current period.
|
||||
*/
|
||||
public boolean willCancelAtPeriodEnd() {
|
||||
return willCancelAtPeriodEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Stripe status of this subscription (see https://stripe.com/docs/billing/subscriptions/overview#subscription-statuses)
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Processor getProcessor() {
|
||||
return processor;
|
||||
}
|
||||
|
||||
public PaymentMethod getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the latest invoice for the subscription is in a non-terminal state
|
||||
*/
|
||||
public boolean isPaymentPending() {
|
||||
return paymentPending;
|
||||
}
|
||||
|
||||
public boolean isFailedPayment() {
|
||||
return Status.isPaymentFailed(getStatus());
|
||||
}
|
||||
|
||||
public boolean isPastDue() {
|
||||
return Status.getStatus(getStatus()) == Status.PAST_DUE;
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return Status.getStatus(getStatus()) == Status.CANCELED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Subscription that = (Subscription) o;
|
||||
return level == that.level && endOfCurrentPeriod == that.endOfCurrentPeriod && isActive == that.isActive && billingCycleAnchor == that.billingCycleAnchor && willCancelAtPeriodEnd == that.willCancelAtPeriodEnd && currency
|
||||
.equals(that.currency) && amount.equals(that.amount) && status.equals(that.status) && Objects.equals(paymentMethod, that.paymentMethod) && paymentPending == that.paymentPending;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(level, currency, amount, endOfCurrentPeriod, isActive, billingCycleAnchor, willCancelAtPeriodEnd, status, paymentMethod, paymentPending);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ChargeFailure {
|
||||
private final String code;
|
||||
private final String message;
|
||||
private final String outcomeNetworkStatus;
|
||||
private final String outcomeNetworkReason;
|
||||
private final String outcomeType;
|
||||
|
||||
@JsonCreator
|
||||
public ChargeFailure(@JsonProperty("code") String code,
|
||||
@JsonProperty("message") String message,
|
||||
@JsonProperty("outcomeNetworkStatus") String outcomeNetworkStatus,
|
||||
@JsonProperty("outcomeNetworkReason") String outcomeNetworkReason,
|
||||
@JsonProperty("outcomeType") String outcomeType)
|
||||
{
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.outcomeNetworkStatus = outcomeNetworkStatus;
|
||||
this.outcomeNetworkReason = outcomeNetworkReason;
|
||||
this.outcomeType = outcomeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error code explaining reason for charge failure if available (see the errors section for a list of codes).
|
||||
* <p>
|
||||
* See: <a href="https://stripe.com/docs/api/charges/object#charge_object-failure_code">https://stripe.com/docs/api/charges/object#charge_object-failure_code</a>
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to user further explaining reason for charge failure if available.
|
||||
* <p>
|
||||
* See: <a href="https://stripe.com/docs/api/charges/object#charge_object-failure_message">https://stripe.com/docs/api/charges/object#charge_object-failure_message</a>
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible values are approved_by_network, declined_by_network, not_sent_to_network, and reversed_after_approval.
|
||||
* The value reversed_after_approval indicates the payment was blocked by Stripe after bank authorization,
|
||||
* and may temporarily appear as "pending" on a cardholder's statement.
|
||||
* <p>
|
||||
* See: <a href="https://stripe.com/docs/api/charges/object#charge_object-outcome-network_status">https://stripe.com/docs/api/charges/object#charge_object-outcome-network_status</a>
|
||||
*/
|
||||
public String getOutcomeNetworkStatus() {
|
||||
return outcomeNetworkStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumerated value providing a more detailed explanation of the outcome's type. Charges blocked by Radar's default block rule have the value
|
||||
* highest_risk_level. Charges placed in review by Radar's default review rule have the value elevated_risk_level. Charges authorized, blocked, or placed
|
||||
* in review by custom rules have the value rule. See understanding declines for more details.
|
||||
* <p>
|
||||
* See: <a href="https://stripe.com/docs/api/charges/object#charge_object-outcome-reason">https://stripe.com/docs/api/charges/object#charge_object-outcome-reason</a>
|
||||
*/
|
||||
public @Nullable String getOutcomeNetworkReason() {
|
||||
return outcomeNetworkReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible values are authorized, manual_review, issuer_declined, blocked, and invalid. See understanding declines and Radar reviews for details.
|
||||
* <p>
|
||||
* See: <a href="https://stripe.com/docs/api/charges/object#charge_object-outcome-type">https://stripe.com/docs/api/charges/object#charge_object-outcome-type</a>
|
||||
*/
|
||||
public String getOutcomeType() {
|
||||
return outcomeType;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "ChargeFailure{" +
|
||||
"code='" + code + '\'' +
|
||||
", outcomeNetworkStatus='" + outcomeNetworkStatus + '\'' +
|
||||
", outcomeNetworkReason='" + outcomeNetworkReason + '\'' +
|
||||
", outcomeType='" + outcomeType + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.subscriptions
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import java.math.BigDecimal
|
||||
|
||||
class ActiveSubscription(
|
||||
@JsonProperty("subscription") val activeSubscription: Subscription? = null,
|
||||
val chargeFailure: ChargeFailure? = null
|
||||
) {
|
||||
|
||||
val isActive: Boolean
|
||||
get() = activeSubscription != null && activeSubscription.isActive
|
||||
|
||||
val isPendingBankTransfer: Boolean
|
||||
get() = activeSubscription != null && activeSubscription.paymentMethod == PaymentMethod.SEPA_DEBIT && activeSubscription.paymentPending
|
||||
|
||||
val isInProgress: Boolean
|
||||
get() = activeSubscription != null && !isActive && (!isFailedPayment || isPastDue) && !isCanceled
|
||||
|
||||
val isPastDue: Boolean
|
||||
get() = activeSubscription != null && activeSubscription.isPastDue
|
||||
|
||||
val isFailedPayment: Boolean
|
||||
get() = chargeFailure != null || (activeSubscription != null && !isActive && activeSubscription.isFailedPayment)
|
||||
|
||||
val isCanceled: Boolean
|
||||
get() = activeSubscription != null && activeSubscription.isCanceled
|
||||
|
||||
/**
|
||||
* Backups-specific call that gives us a value that should align with autoRenew from the GPB payment.
|
||||
*/
|
||||
val willCancelAtPeriodEnd: Boolean
|
||||
get() = activeSubscription == null || activeSubscription.willCancelAtPeriodEnd
|
||||
|
||||
enum class Processor(val code: String) {
|
||||
STRIPE("STRIPE"),
|
||||
BRAINTREE("BRAINTREE"),
|
||||
GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING");
|
||||
|
||||
companion object {
|
||||
fun fromCode(code: String?): Processor = entries.firstOrNull { it.code == code } ?: STRIPE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As per API documentation
|
||||
*/
|
||||
enum class PaymentMethod(val code: String) {
|
||||
UNKNOWN("UNKNOWN"),
|
||||
CARD("CARD"),
|
||||
PAYPAL("PAYPAL"),
|
||||
SEPA_DEBIT("SEPA_DEBIT"),
|
||||
IDEAL("IDEAL"),
|
||||
GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"),
|
||||
APPLE_APP_STORE("APPLE_APP_STORE");
|
||||
|
||||
companion object {
|
||||
fun fromCode(code: String?): PaymentMethod = entries.firstOrNull { it.code == code } ?: UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
enum class Status(val code: String) {
|
||||
/**
|
||||
* The subscription is currently in a trial period and it's safe to provision your product for your customer.
|
||||
* The subscription transitions automatically to active when the first payment is made.
|
||||
*/
|
||||
TRIALING("trialing"),
|
||||
|
||||
/**
|
||||
* The subscription is in good standing and the most recent payment was successful. It's safe to provision your product for your customer.
|
||||
*/
|
||||
ACTIVE("active"),
|
||||
|
||||
/**
|
||||
* Payment failed when you created the subscription. A successful payment needs to be made within 23 hours to activate the subscription.
|
||||
*/
|
||||
INCOMPLETE("incomplete"),
|
||||
|
||||
/**
|
||||
* The initial payment on the subscription failed and no successful payment was made within 23 hours of creating the subscription.
|
||||
* These subscriptions don't bill customers. This status exists so you can track customers that failed to activate their subscriptions.
|
||||
*/
|
||||
INCOMPLETE_EXPIRED("incomplete_expired"),
|
||||
|
||||
/**
|
||||
* Payment on the latest invoice either failed or wasn't attempted.
|
||||
*/
|
||||
PAST_DUE("past_due"),
|
||||
|
||||
/**
|
||||
* The subscription has been canceled. During cancellation, automatic collection for all unpaid invoices is disabled (auto_advance=false).
|
||||
*/
|
||||
CANCELED("canceled"),
|
||||
|
||||
/**
|
||||
* The latest invoice hasn't been paid but the subscription remains in place.
|
||||
* The latest invoice remains open and invoices continue to be generated but payments aren't attempted.
|
||||
*/
|
||||
UNPAID("unpaid");
|
||||
|
||||
companion object {
|
||||
private val FAILURE_STATUSES = setOf(INCOMPLETE_EXPIRED, PAST_DUE, UNPAID)
|
||||
|
||||
@JvmStatic
|
||||
fun getStatus(status: String?): Status {
|
||||
return entries.firstOrNull { it.code == status } ?: throw IllegalArgumentException("Unknown status $status")
|
||||
}
|
||||
|
||||
internal fun isPaymentFailed(status: String?): Boolean = FAILURE_STATUSES.contains(getStatus(status))
|
||||
}
|
||||
}
|
||||
|
||||
data class Subscription(
|
||||
val level: Int = 0,
|
||||
val currency: String = "",
|
||||
val amount: BigDecimal = BigDecimal.ZERO,
|
||||
/** UNIX Epoch Timestamp in seconds */
|
||||
val endOfCurrentPeriod: Long = 0,
|
||||
/** Whether this subscription is currently active. */
|
||||
@JsonProperty("active") val isActive: Boolean = false,
|
||||
/**
|
||||
* UNIX Epoch Timestamp in seconds, can be used to calculate next billing date per
|
||||
* https://stripe.com/docs/billing/subscriptions/billing-cycle
|
||||
*/
|
||||
val billingCycleAnchor: Long = 0,
|
||||
/** Whether this subscription is set to end at the end of the current period. */
|
||||
@JsonProperty("cancelAtPeriodEnd") val willCancelAtPeriodEnd: Boolean = false,
|
||||
/** The Stripe status of this subscription (see https://stripe.com/docs/billing/subscriptions/overview#subscription-statuses) */
|
||||
val status: String = "",
|
||||
@JsonProperty("processor") private val processorCode: String? = null,
|
||||
@JsonProperty("paymentMethod") private val paymentMethodCode: String? = null,
|
||||
/** Whether the latest invoice for the subscription is in a non-terminal state. */
|
||||
val paymentPending: Boolean = false
|
||||
) {
|
||||
val processor: Processor
|
||||
get() = Processor.fromCode(processorCode)
|
||||
|
||||
val paymentMethod: PaymentMethod
|
||||
get() = PaymentMethod.fromCode(paymentMethodCode)
|
||||
|
||||
val isFailedPayment: Boolean
|
||||
get() = Status.isPaymentFailed(status)
|
||||
|
||||
val isPastDue: Boolean
|
||||
get() = Status.getStatus(status) == Status.PAST_DUE
|
||||
|
||||
val isCanceled: Boolean
|
||||
get() = Status.getStatus(status) == Status.CANCELED
|
||||
}
|
||||
|
||||
data class ChargeFailure(
|
||||
/**
|
||||
* Error code explaining reason for charge failure if available (see the errors section for a list of codes).
|
||||
*
|
||||
* See: [https://stripe.com/docs/api/charges/object#charge_object-failure_code]
|
||||
*/
|
||||
val code: String? = null,
|
||||
|
||||
/**
|
||||
* Message to user further explaining reason for charge failure if available.
|
||||
*
|
||||
* See: [https://stripe.com/docs/api/charges/object#charge_object-failure_message]
|
||||
*/
|
||||
val message: String? = null,
|
||||
|
||||
/**
|
||||
* Possible values are approved_by_network, declined_by_network, not_sent_to_network, and reversed_after_approval.
|
||||
* The value reversed_after_approval indicates the payment was blocked by Stripe after bank authorization,
|
||||
* and may temporarily appear as "pending" on a cardholder's statement.
|
||||
*
|
||||
* See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-network_status]
|
||||
*/
|
||||
val outcomeNetworkStatus: String? = null,
|
||||
|
||||
/**
|
||||
* An enumerated value providing a more detailed explanation of the outcome's type. Charges blocked by Radar's default block rule have the value
|
||||
* highest_risk_level. Charges placed in review by Radar's default review rule have the value elevated_risk_level. Charges authorized, blocked, or placed
|
||||
* in review by custom rules have the value rule. See understanding declines for more details.
|
||||
*
|
||||
* See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-reason]
|
||||
*/
|
||||
val outcomeNetworkReason: String? = null,
|
||||
|
||||
/**
|
||||
* Possible values are authorized, manual_review, issuer_declined, blocked, and invalid. See understanding declines and Radar reviews for details.
|
||||
*
|
||||
* See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-type]
|
||||
*/
|
||||
val outcomeType: String? = null
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "ChargeFailure{code='$code', outcomeNetworkStatus='$outcomeNetworkStatus', outcomeNetworkReason='$outcomeNetworkReason', outcomeType='$outcomeType'}"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAYMENT_METHOD_SEPA_DEBIT = "SEPA_DEBIT"
|
||||
|
||||
@JvmField
|
||||
val EMPTY = ActiveSubscription(null, null)
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.subscriptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Response object from creating a payment intent via PayPal
|
||||
*/
|
||||
public class PayPalConfirmPaymentIntentResponse {
|
||||
|
||||
private final String paymentId;
|
||||
|
||||
@JsonCreator
|
||||
public PayPalConfirmPaymentIntentResponse(@JsonProperty("paymentId") String paymentId) {
|
||||
this.paymentId = paymentId;
|
||||
}
|
||||
|
||||
public String getPaymentId() {
|
||||
return paymentId;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.subscriptions
|
||||
|
||||
/**
|
||||
* Response object from confirming a payment intent via PayPal
|
||||
*/
|
||||
data class PayPalConfirmPaymentIntentResponse(
|
||||
val paymentId: String
|
||||
)
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.subscriptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Response object from creating a payment intent via PayPal
|
||||
*/
|
||||
public class PayPalCreatePaymentIntentResponse {
|
||||
|
||||
private final String approvalUrl;
|
||||
private final String paymentId;
|
||||
|
||||
@JsonCreator
|
||||
public PayPalCreatePaymentIntentResponse(@JsonProperty("approvalUrl") String approvalUrl, @JsonProperty("paymentId") String paymentId) {
|
||||
this.approvalUrl = approvalUrl;
|
||||
this.paymentId = paymentId;
|
||||
}
|
||||
|
||||
public String getApprovalUrl() {
|
||||
return approvalUrl;
|
||||
}
|
||||
|
||||
public String getPaymentId() {
|
||||
return paymentId;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.subscriptions
|
||||
|
||||
/**
|
||||
* Response object from creating a payment intent via PayPal
|
||||
*/
|
||||
data class PayPalCreatePaymentIntentResponse(
|
||||
val approvalUrl: String,
|
||||
val paymentId: String
|
||||
)
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.subscriptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class PayPalCreatePaymentMethodResponse {
|
||||
private final String approvalUrl;
|
||||
private final String token;
|
||||
|
||||
@JsonCreator
|
||||
public PayPalCreatePaymentMethodResponse(@JsonProperty("approvalUrl") String approvalUrl, @JsonProperty("token") String token) {
|
||||
this.approvalUrl = approvalUrl;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getApprovalUrl() {
|
||||
return approvalUrl;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.subscriptions
|
||||
|
||||
/**
|
||||
* Response object from creating a payment method via PayPal
|
||||
*/
|
||||
data class PayPalCreatePaymentMethodResponse(
|
||||
val approvalUrl: String,
|
||||
val token: String
|
||||
)
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.subscriptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public final class StripeClientSecret {
|
||||
|
||||
private final String id;
|
||||
private final String clientSecret;
|
||||
|
||||
@JsonCreator
|
||||
public StripeClientSecret(@JsonProperty("clientSecret") String clientSecret) {
|
||||
this.id = clientSecret.replaceFirst("_secret.*", "");
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.subscriptions
|
||||
|
||||
data class StripeClientSecret(
|
||||
val clientSecret: String
|
||||
) {
|
||||
val id: String
|
||||
get() = clientSecret.replaceFirst("_secret.*".toRegex(), "")
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import okhttp3.Credentials;
|
||||
|
||||
public class AuthCredentials {
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
@JsonProperty
|
||||
private String password;
|
||||
|
||||
public static AuthCredentials create(String username, String password) {
|
||||
AuthCredentials authCredentials = new AuthCredentials();
|
||||
authCredentials.username = username;
|
||||
authCredentials.password = password;
|
||||
return authCredentials;
|
||||
}
|
||||
|
||||
public String asBasic() {
|
||||
return Credentials.basic(username, password);
|
||||
}
|
||||
|
||||
public String username() { return username; }
|
||||
|
||||
public String password() { return password; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthCredentials(xxx)";
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import okhttp3.Credentials
|
||||
|
||||
class AuthCredentials(
|
||||
@field:JsonProperty private val username: String,
|
||||
@field:JsonProperty private val password: String
|
||||
) {
|
||||
fun asBasic(): String = Credentials.basic(username, password)
|
||||
|
||||
fun username(): String = username
|
||||
|
||||
fun password(): String = password
|
||||
|
||||
override fun toString(): String = "AuthCredentials(xxx)"
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(username: String, password: String): AuthCredentials = AuthCredentials(username, password)
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CdsiAuthResponse {
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
@JsonProperty
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
data class CdsiAuthResponse(
|
||||
val username: String,
|
||||
val password: String
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class ConfirmUsernameRequest {
|
||||
@JsonProperty
|
||||
private String usernameHash;
|
||||
|
||||
@JsonProperty
|
||||
private String zkProof;
|
||||
|
||||
@JsonProperty
|
||||
private String encryptedUsername;
|
||||
|
||||
public ConfirmUsernameRequest(String usernameHash, String zkProof, String encryptedUsername) {
|
||||
this.usernameHash = usernameHash;
|
||||
this.zkProof = zkProof;
|
||||
this.encryptedUsername = encryptedUsername;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
class ConfirmUsernameRequest(
|
||||
val usernameHash: String,
|
||||
val zkProof: String,
|
||||
val encryptedUsername: String
|
||||
)
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DeviceInfoList {
|
||||
|
||||
@JsonProperty
|
||||
public List<DeviceInfo> devices;
|
||||
|
||||
public DeviceInfoList() {}
|
||||
|
||||
public List<DeviceInfo> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo
|
||||
|
||||
class DeviceInfoList(
|
||||
val devices: List<DeviceInfo> = emptyList()
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DeviceLimit {
|
||||
|
||||
@JsonProperty
|
||||
private int current;
|
||||
|
||||
@JsonProperty
|
||||
private int max;
|
||||
|
||||
public int getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return max;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
data class DeviceLimit(
|
||||
val current: Int = 0,
|
||||
val max: Int = 0
|
||||
)
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DonationIntentResult {
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("client_secret")
|
||||
private String clientSecret;
|
||||
|
||||
public DonationIntentResult(@JsonProperty("id") String id, @JsonProperty("client_secret") String clientSecret) {
|
||||
this.id = id;
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
data class DonationIntentResult(
|
||||
val id: String,
|
||||
@JsonProperty("client_secret") val clientSecret: String
|
||||
)
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Represents the body of a 409 response from the service during a sender key send.
|
||||
*/
|
||||
public class GroupMismatchedDevices {
|
||||
@JsonProperty
|
||||
private String uuid;
|
||||
|
||||
@JsonProperty
|
||||
private MismatchedDevices devices;
|
||||
|
||||
public GroupMismatchedDevices() {}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public MismatchedDevices getDevices() {
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
/**
|
||||
* Represents the body of a 409 response from the service during a sender key send.
|
||||
*/
|
||||
data class GroupMismatchedDevices(
|
||||
val uuid: String,
|
||||
val devices: MismatchedDevices
|
||||
)
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Represents the body of a 410 response from the service during a sender key send.
|
||||
*/
|
||||
public class GroupStaleDevices {
|
||||
|
||||
@JsonProperty
|
||||
private String uuid;
|
||||
|
||||
@JsonProperty
|
||||
private StaleDevices devices;
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public StaleDevices getDevices() {
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user