Add ability to Self-heal SEPA bug.

This commit is contained in:
Alex Hart
2025-03-26 16:11:49 -03:00
committed by GitHub
parent de54ff304d
commit 0358631029
2 changed files with 34 additions and 3 deletions

View File

@@ -162,6 +162,7 @@ class InAppPaymentKeepAliveJob private constructor(
SignalDatabase.inAppPayments.update(payment)
InAppPaymentRecurringContextJob.createJobChain(payment).enqueue()
}
InAppPaymentData.RedemptionState.Stage.CONVERSION_STARTED -> {
if (activeInAppPayment.data.redemption.receiptCredentialRequestContext == null) {
warn(type, "We are in the CONVERSION_STARTED state without a request credential. Exiting.")
@@ -171,6 +172,7 @@ class InAppPaymentKeepAliveJob private constructor(
info(type, "We have a request credential we have not turned into a token.")
InAppPaymentRecurringContextJob.createJobChain(activeInAppPayment).enqueue()
}
InAppPaymentData.RedemptionState.Stage.REDEMPTION_STARTED -> {
if (activeInAppPayment.data.redemption.receiptCredentialPresentation == null) {
warn(type, "We are in the REDEMPTION_STARTED state without a request credential. Exiting.")
@@ -180,6 +182,7 @@ class InAppPaymentKeepAliveJob private constructor(
info(type, "We have a receipt credential presentation but have not yet redeemed it.")
InAppPaymentRedemptionJob.enqueueJobChainForRecurringKeepAlive(activeInAppPayment)
}
else -> info(type, "Nothing to do. Exiting.")
}
}
@@ -197,6 +200,7 @@ class InAppPaymentKeepAliveJob private constructor(
403, 404 -> {
warn(type, "Invalid or malformed subscriber id. Status: ${serviceResponse.status}", error)
}
else -> {
warn(type, "An unknown server error occurred: ${serviceResponse.status}", error)
throw InAppPaymentRetryException(error)
@@ -213,7 +217,7 @@ class InAppPaymentKeepAliveJob private constructor(
val type = subscriber.type
val current: InAppPaymentTable.InAppPayment? = SignalDatabase.inAppPayments.getByEndOfPeriod(type.inAppPaymentType, endOfCurrentPeriod)
return if (current == null) {
val inAppPayment = if (current == null) {
val oldInAppPayment = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(type.inAppPaymentType)
val oldEndOfPeriod = oldInAppPayment?.endOfPeriod ?: InAppPaymentsRepository.getFallbackLastEndOfPeriod(type)
if (oldEndOfPeriod > endOfCurrentPeriod) {
@@ -256,9 +260,11 @@ class InAppPaymentKeepAliveJob private constructor(
InAppPaymentData.PaymentMethodType.CARD
}
}
InAppPaymentData.PaymentMethodType.UNKNOWN -> {
subscriberPaymentMethodType
}
else -> subscriptionPaymentMethodType
}
@@ -318,6 +324,31 @@ class InAppPaymentKeepAliveJob private constructor(
} else {
current
}
if (
inAppPayment != null &&
inAppPayment.state == InAppPaymentTable.State.END &&
ActiveSubscription.Status.getStatus(subscription.status) == ActiveSubscription.Status.ACTIVE &&
inAppPayment.endOfPeriodSeconds == subscription.endOfCurrentPeriod &&
inAppPayment.data.error != null &&
inAppPayment.data.redemption?.stage == InAppPaymentData.RedemptionState.Stage.CONVERSION_STARTED &&
inAppPayment.data.paymentMethodType == InAppPaymentData.PaymentMethodType.SEPA_DEBIT
) {
warn(type, "Detected possible timeout failure due to SEPA bug. We will resubmit.")
SignalDatabase.inAppPayments.update(
inAppPayment.copy(
state = InAppPaymentTable.State.PENDING,
data = inAppPayment.data.newBuilder()
.error(null)
.build()
)
)
return SignalDatabase.inAppPayments.getById(inAppPayment.id)
} else {
return inAppPayment
}
}
private fun info(type: InAppPaymentSubscriberRecord.Type, message: String) {

View File

@@ -72,7 +72,7 @@ public final class ActiveSubscription {
}
}
private enum Status {
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.
@@ -123,7 +123,7 @@ public final class ActiveSubscription {
this.status = status;
}
private static Status getStatus(String status) {
public static Status getStatus(String status) {
for (Status s : Status.values()) {
if (Objects.equals(status, s.status)) {
return s;