kopia lustrzana https://github.com/ryukoposting/Signal-Android
Improve recognition of failed payment states.
rodzic
1508b1d401
commit
cd1f0632fa
|
@ -60,7 +60,7 @@ public class BoostReceiptRequestResponseJob extends BaseJob {
|
|||
public static JobManager.Chain createJobChain(StripeApi.PaymentIntent paymentIntent) {
|
||||
BoostReceiptRequestResponseJob requestReceiptJob = createJob(paymentIntent);
|
||||
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForBoost();
|
||||
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
||||
RefreshOwnProfileJob refreshOwnProfileJob = RefreshOwnProfileJob.forBoost();
|
||||
|
||||
return ApplicationDependencies.getJobManager()
|
||||
.startChain(requestReceiptJob)
|
||||
|
@ -195,19 +195,19 @@ public class BoostReceiptRequestResponseJob extends BaseJob {
|
|||
* - expiration_time is between now and 60 days from now
|
||||
*/
|
||||
private boolean isCredentialValid(@NonNull ReceiptCredential receiptCredential) {
|
||||
long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
|
||||
long monthFromNow = now + TimeUnit.DAYS.toSeconds(60);
|
||||
boolean isCorrectLevel = receiptCredential.getReceiptLevel() == 1;
|
||||
boolean isExpiration86400 = receiptCredential.getReceiptExpirationTime() % 86400 == 0;
|
||||
boolean isExpirationInTheFuture = receiptCredential.getReceiptExpirationTime() > now;
|
||||
boolean isExpirationWithinAMonth = receiptCredential.getReceiptExpirationTime() <= monthFromNow;
|
||||
long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
|
||||
long maxExpirationTime = now + TimeUnit.DAYS.toSeconds(60);
|
||||
boolean isCorrectLevel = receiptCredential.getReceiptLevel() == 1;
|
||||
boolean isExpiration86400 = receiptCredential.getReceiptExpirationTime() % 86400 == 0;
|
||||
boolean isExpirationInTheFuture = receiptCredential.getReceiptExpirationTime() > now;
|
||||
boolean isExpirationWithinMax = receiptCredential.getReceiptExpirationTime() <= maxExpirationTime;
|
||||
|
||||
Log.d(TAG, "Credential validation: isCorrectLevel(" + isCorrectLevel +
|
||||
") isExpiration86400(" + isExpiration86400 +
|
||||
") isExpirationInTheFuture(" + isExpirationInTheFuture +
|
||||
") isExpirationWithinAMonth(" + isExpirationWithinAMonth + ")", true);
|
||||
") isExpirationWithinMax(" + isExpirationWithinMax + ")", true);
|
||||
|
||||
return isCorrectLevel && isExpiration86400 && isExpirationInTheFuture && isExpirationWithinAMonth;
|
||||
return isCorrectLevel && isExpiration86400 && isExpirationInTheFuture && isExpirationWithinMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,13 +48,28 @@ public class RefreshOwnProfileJob extends BaseJob {
|
|||
|
||||
private static final String TAG = Log.tag(RefreshOwnProfileJob.class);
|
||||
|
||||
private static final String SUBSCRIPTION_QUEUE = ProfileUploadJob.QUEUE + "_Subscription";
|
||||
private static final String BOOST_QUEUE = ProfileUploadJob.QUEUE + "_Boost";
|
||||
|
||||
public RefreshOwnProfileJob() {
|
||||
this(ProfileUploadJob.QUEUE);
|
||||
}
|
||||
|
||||
private RefreshOwnProfileJob(@NonNull String queue) {
|
||||
this(new Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(ProfileUploadJob.QUEUE)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(10)
|
||||
.build());
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(queue)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(10)
|
||||
.build());
|
||||
}
|
||||
|
||||
public static @NonNull RefreshOwnProfileJob forSubscription() {
|
||||
return new RefreshOwnProfileJob(SUBSCRIPTION_QUEUE);
|
||||
}
|
||||
|
||||
public static @NonNull RefreshOwnProfileJob forBoost() {
|
||||
return new RefreshOwnProfileJob(BOOST_QUEUE);
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,11 +213,11 @@ public class RefreshOwnProfileJob extends BaseJob {
|
|||
.max(Comparator.comparingLong(Badge::getExpirationTimestamp))
|
||||
.get();
|
||||
|
||||
Log.d(TAG, "Marking subscription badge as expired, should notifiy next time the conversation list is open.");
|
||||
Log.d(TAG, "Marking subscription badge as expired, should notify next time the conversation list is open.", true);
|
||||
SignalStore.donationsValues().setExpiredBadge(mostRecentExpiration);
|
||||
|
||||
if (!SignalStore.donationsValues().isUserManuallyCancelled()) {
|
||||
Log.d(TAG, "Detected an unexpected subscription expiry.");
|
||||
Log.d(TAG, "Detected an unexpected subscription expiry.", true);
|
||||
SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true);
|
||||
}
|
||||
} else if (!remoteHasBoostBadges && localHasBoostBadges) {
|
||||
|
@ -214,7 +229,7 @@ public class RefreshOwnProfileJob extends BaseJob {
|
|||
.max(Comparator.comparingLong(Badge::getExpirationTimestamp))
|
||||
.get();
|
||||
|
||||
Log.d(TAG, "Marking boost badge as expired, should notifiy next time the conversation list is open.");
|
||||
Log.d(TAG, "Marking boost badge as expired, should notify next time the conversation list is open.", true);
|
||||
SignalStore.donationsValues().setExpiredBadge(mostRecentExpiration);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
Subscriber subscriber = SignalStore.donationsValues().requireSubscriber();
|
||||
SubscriptionReceiptRequestResponseJob requestReceiptJob = createJob(subscriber.getSubscriberId());
|
||||
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForSubscription();
|
||||
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
||||
RefreshOwnProfileJob refreshOwnProfileJob = RefreshOwnProfileJob.forSubscription();
|
||||
|
||||
return ApplicationDependencies.getJobManager()
|
||||
.startChain(requestReceiptJob)
|
||||
|
@ -115,14 +115,16 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
|
||||
private void doRun() throws Exception {
|
||||
ActiveSubscription.Subscription subscription = getLatestSubscriptionInformation();
|
||||
if (subscription == null || !subscription.isActive()) {
|
||||
Log.w(TAG, "User does not have an active subscription yet.", true);
|
||||
if (subscription == null) {
|
||||
Log.w(TAG, "Subscription is null.", true);
|
||||
throw new RetryableException();
|
||||
} else if (subscription.isFailedPayment()) {
|
||||
Log.w(TAG, "Subscription payment failure. Passing through to redemption job.", true);
|
||||
SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true);
|
||||
setOutputData(new Data.Builder().putBoolean(DonationReceiptRedemptionJob.INPUT_PAYMENT_FAILURE, true).build());
|
||||
Log.w(TAG, "Subscription payment failure in active subscription response (status = " + subscription.getStatus() + "). Passing through to redemption job.", true);
|
||||
onPaymentFailure();
|
||||
return;
|
||||
} else if (!subscription.isActive()) {
|
||||
Log.w(TAG, "Subscription is not yet active. Status: " + subscription.getStatus(), true);
|
||||
throw new RetryableException();
|
||||
} else {
|
||||
Log.i(TAG, "Recording end of period from active subscription.", true);
|
||||
SignalStore.donationsValues().setLastEndOfPeriod(subscription.getEndOfCurrentPeriod());
|
||||
|
@ -150,10 +152,6 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
|
||||
if (response.getApplicationError().isPresent()) {
|
||||
handleApplicationError(response);
|
||||
|
||||
if (response.getStatus() == 204) {
|
||||
SignalStore.donationsValues().clearSubscriptionRedemptionFailed();
|
||||
}
|
||||
} else if (response.getResult().isPresent()) {
|
||||
ReceiptCredential receiptCredential = getReceiptCredential(response.getResult().get());
|
||||
|
||||
|
@ -214,15 +212,15 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
private void handleApplicationError(ServiceResponse<ReceiptCredentialResponse> response) throws Exception {
|
||||
switch (response.getStatus()) {
|
||||
case 204:
|
||||
Log.w(TAG, "User does not have receipts available to exchange. Exiting.", response.getApplicationError().get(), true);
|
||||
break;
|
||||
Log.w(TAG, "Payment is still processing. Trying again.", response.getApplicationError().get(), true);
|
||||
SignalStore.donationsValues().clearSubscriptionRedemptionFailed();
|
||||
throw new RetryableException();
|
||||
case 400:
|
||||
Log.w(TAG, "Receipt credential request failed to validate.", response.getApplicationError().get(), true);
|
||||
throw new Exception(response.getApplicationError().get());
|
||||
case 402:
|
||||
Log.w(TAG, "Subscription payment failure. Passing through to redemption job.", true);
|
||||
SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true);
|
||||
setOutputData(new Data.Builder().putBoolean(DonationReceiptRedemptionJob.INPUT_PAYMENT_FAILURE, true).build());
|
||||
Log.w(TAG, "Subscription payment failure in credential response. Passing through to redemption job.", true);
|
||||
onPaymentFailure();
|
||||
break;
|
||||
case 403:
|
||||
Log.w(TAG, "SubscriberId password mismatch or account auth was present.", response.getApplicationError().get(), true);
|
||||
|
@ -239,6 +237,11 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void onPaymentFailure() {
|
||||
SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true);
|
||||
setOutputData(new Data.Builder().putBoolean(DonationReceiptRedemptionJob.INPUT_PAYMENT_FAILURE, true).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the generated Receipt Credential has the following characteristics
|
||||
* - level should match the current subscription level and be the same level you signed up for at the time the subscription was last updated
|
||||
|
@ -247,21 +250,21 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
* - expiration_time is between now and 60 days from now
|
||||
*/
|
||||
private boolean isCredentialValid(@NonNull ActiveSubscription.Subscription subscription, @NonNull ReceiptCredential receiptCredential) {
|
||||
long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
|
||||
long monthFromNow = now + TimeUnit.DAYS.toSeconds(60);
|
||||
boolean isSameLevel = subscription.getLevel() == receiptCredential.getReceiptLevel();
|
||||
boolean isExpirationAfterSub = subscription.getEndOfCurrentPeriod() < receiptCredential.getReceiptExpirationTime();
|
||||
boolean isExpiration86400 = receiptCredential.getReceiptExpirationTime() % 86400 == 0;
|
||||
boolean isExpirationInTheFuture = receiptCredential.getReceiptExpirationTime() > now;
|
||||
boolean isExpirationWithinAMonth = receiptCredential.getReceiptExpirationTime() <= monthFromNow;
|
||||
long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
|
||||
long maxExpirationTime = now + TimeUnit.DAYS.toSeconds(60);
|
||||
boolean isSameLevel = subscription.getLevel() == receiptCredential.getReceiptLevel();
|
||||
boolean isExpirationAfterSub = subscription.getEndOfCurrentPeriod() < receiptCredential.getReceiptExpirationTime();
|
||||
boolean isExpiration86400 = receiptCredential.getReceiptExpirationTime() % 86400 == 0;
|
||||
boolean isExpirationInTheFuture = receiptCredential.getReceiptExpirationTime() > now;
|
||||
boolean isExpirationWithinMax = receiptCredential.getReceiptExpirationTime() <= maxExpirationTime;
|
||||
|
||||
Log.d(TAG, "Credential validation: isSameLevel(" + isSameLevel +
|
||||
") isExpirationAfterSub(" + isExpirationAfterSub +
|
||||
") isExpiration86400(" + isExpiration86400 +
|
||||
") isExpirationInTheFuture(" + isExpirationInTheFuture +
|
||||
") isExpirationWithinAMonth(" + isExpirationWithinAMonth + ")", true);
|
||||
") isExpirationWithinMax(" + isExpirationWithinMax + ")", true);
|
||||
|
||||
return isSameLevel && isExpirationAfterSub && isExpiration86400 && isExpirationInTheFuture && isExpirationWithinAMonth;
|
||||
return isSameLevel && isExpirationAfterSub && isExpiration86400 && isExpirationInTheFuture && isExpirationWithinMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,7 +4,10 @@ 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;
|
||||
|
||||
public final class ActiveSubscription {
|
||||
|
||||
|
@ -49,6 +52,13 @@ public final class ActiveSubscription {
|
|||
|
||||
private final String status;
|
||||
|
||||
private static final Set<Status> FAILURE_STATUSES = new HashSet<>(Arrays.asList(
|
||||
INCOMPLETE_EXPIRED,
|
||||
PAST_DUE,
|
||||
CANCELED,
|
||||
UNPAID
|
||||
));
|
||||
|
||||
Status(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
@ -64,8 +74,7 @@ public final class ActiveSubscription {
|
|||
}
|
||||
|
||||
static boolean isPaymentFailed(String status) {
|
||||
Status s = getStatus(status);
|
||||
return s == INCOMPLETE || s == INCOMPLETE_EXPIRED;
|
||||
return FAILURE_STATUSES.contains(getStatus(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,11 +94,11 @@ public final class ActiveSubscription {
|
|||
}
|
||||
|
||||
public boolean isInProgress() {
|
||||
return activeSubscription != null && !isActive() && !isFailedPayment();
|
||||
return activeSubscription != null && !isActive() && !activeSubscription.isFailedPayment();
|
||||
}
|
||||
|
||||
public boolean isFailedPayment() {
|
||||
return activeSubscription != null && !isActive() && isFailedPayment();
|
||||
return activeSubscription != null && !isActive() && activeSubscription.isFailedPayment();
|
||||
}
|
||||
|
||||
public static final class Subscription {
|
||||
|
@ -171,8 +180,7 @@ public final class ActiveSubscription {
|
|||
}
|
||||
|
||||
public boolean isInProgress() {
|
||||
return !isActive() &&
|
||||
!Status.isPaymentFailed(getStatus());
|
||||
return !isActive() && !Status.isPaymentFailed(getStatus());
|
||||
}
|
||||
|
||||
public boolean isFailedPayment() {
|
||||
|
|
Ładowanie…
Reference in New Issue