diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java
index 475a90a3310ce72bda9889e0d74e7799ff5a261f..bcaa2a5a6053215bcb250c87076ae3aa1146d52d 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java
@@ -802,9 +802,11 @@ public class SubscriptionController {
     public SubscriptionProcessor processor = SubscriptionProcessor.STRIPE;
   }
 
-  public record CreateBoostReceiptCredentialsResponse(byte[] receiptCredentialResponse) {
+  public record CreateBoostReceiptCredentialsSuccessResponse(byte[] receiptCredentialResponse) {
   }
 
+  public record CreateBoostReceiptCredentialsErrorResponse(@JsonInclude(Include.NON_NULL) ChargeFailure chargeFailure) {}
+
   @POST
   @Path("/boost/receipt_credentials")
   @Consumes(MediaType.APPLICATION_JSON)
@@ -824,7 +826,8 @@ public class SubscriptionController {
             case PROCESSING -> throw new WebApplicationException(Status.NO_CONTENT);
             case SUCCEEDED -> {
             }
-            default -> throw new WebApplicationException(Status.PAYMENT_REQUIRED);
+            default -> throw new WebApplicationException(Response.status(Status.PAYMENT_REQUIRED)
+                .entity(new CreateBoostReceiptCredentialsErrorResponse(paymentDetails.chargeFailure())).build());
           }
 
           long level = oneTimeDonationConfiguration.boost().level();
@@ -875,7 +878,7 @@ public class SubscriptionController {
                             Tag.of(TYPE_TAG_NAME, "boost"),
                             UserAgentTagUtil.getPlatformTag(userAgent)))
                     .increment();
-                return Response.ok(new CreateBoostReceiptCredentialsResponse(receiptCredentialResponse.serialize()))
+                return Response.ok(new CreateBoostReceiptCredentialsSuccessResponse(receiptCredentialResponse.serialize()))
                     .build();
               });
         });
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreeManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreeManager.java
index 3d039029f28ec1807a1b19d8c976eccb3c7544d6..4c30a4de8049327c738bfff6faee5bcc6adf7325 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreeManager.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreeManager.java
@@ -117,11 +117,15 @@ public class BraintreeManager implements SubscriptionProcessorManager {
     return CompletableFuture.supplyAsync(() -> {
       try {
         final Transaction transaction = braintreeGateway.transaction().find(paymentId);
-
+        ChargeFailure chargeFailure = null;
+        if (!getPaymentStatus(transaction.getStatus()).equals(PaymentStatus.SUCCEEDED)) {
+          chargeFailure = createChargeFailure(transaction);
+        }
         return new PaymentDetails(transaction.getGraphQLId(),
             transaction.getCustomFields(),
             getPaymentStatus(transaction.getStatus()),
-            transaction.getCreatedAt().toInstant());
+            transaction.getCreatedAt().toInstant(),
+            chargeFailure);
 
       } catch (final NotFoundException e) {
         return null;
@@ -433,7 +437,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
 
       if (latestTransaction.isPresent()){
         paymentProcessing = isPaymentProcessing(latestTransaction.get().getStatus());
-        if (!getPaymentStatus(latestTransaction.get().getStatus()).equals(PaymentStatus.SUCCEEDED)) {
+        if (getPaymentStatus(latestTransaction.get().getStatus()) != PaymentStatus.SUCCEEDED) {
           chargeFailure = createChargeFailure(latestTransaction.get());
         }
       }
@@ -470,7 +474,10 @@ public class BraintreeManager implements SubscriptionProcessorManager {
 
     final String code;
     final String message;
-    if (transaction.getProcessorResponseCode() != null) {
+    if (transaction.getStatus() == Transaction.Status.VOIDED) {
+      code = "voided";
+      message = "voided";
+    } else if (transaction.getProcessorResponseCode() != null) {
       code = transaction.getProcessorResponseCode();
       message = transaction.getProcessorResponseText();
     } else if (transaction.getGatewayRejectionReason() != null) {
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/StripeManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/StripeManager.java
index 35b6fa921ddc3aca82e9bcf05cfc5aac390ec736..a988a460152edc25cda8bec475cdae680c399747 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/StripeManager.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/StripeManager.java
@@ -28,6 +28,7 @@ import com.stripe.param.CustomerUpdateParams;
 import com.stripe.param.CustomerUpdateParams.InvoiceSettings;
 import com.stripe.param.InvoiceListParams;
 import com.stripe.param.PaymentIntentCreateParams;
+import com.stripe.param.PaymentIntentRetrieveParams;
 import com.stripe.param.PriceRetrieveParams;
 import com.stripe.param.SetupIntentCreateParams;
 import com.stripe.param.SubscriptionCancelParams;
@@ -216,12 +217,23 @@ public class StripeManager implements SubscriptionProcessorManager {
   public CompletableFuture<PaymentDetails> getPaymentDetails(String paymentIntentId) {
     return CompletableFuture.supplyAsync(() -> {
       try {
-        final PaymentIntent paymentIntent = stripeClient.paymentIntents().retrieve(paymentIntentId, commonOptions());
+        final PaymentIntentRetrieveParams params = PaymentIntentRetrieveParams.builder()
+            .addExpand("latest_charge").build();
+        final PaymentIntent paymentIntent = stripeClient.paymentIntents().retrieve(paymentIntentId, params, commonOptions());
+
+        ChargeFailure chargeFailure = null;
+        if (paymentIntent.getLatestChargeObject() != null) {
+          final Charge charge = paymentIntent.getLatestChargeObject();
+          if (charge.getFailureCode() != null || charge.getFailureMessage() != null) {
+            chargeFailure = createChargeFailure(charge);
+          }
+        }
 
         return new PaymentDetails(paymentIntent.getId(),
             paymentIntent.getMetadata() == null ? Collections.emptyMap() : paymentIntent.getMetadata(),
             getPaymentStatusForStatus(paymentIntent.getStatus()),
-            Instant.ofEpochSecond(paymentIntent.getCreated()));
+            Instant.ofEpochSecond(paymentIntent.getCreated()),
+            chargeFailure);
       } catch (StripeException e) {
         if (e.getStatusCode() == 404) {
           return null;
@@ -479,6 +491,16 @@ public class StripeManager implements SubscriptionProcessorManager {
     }, executor);
   }
 
+  private static ChargeFailure createChargeFailure(final Charge charge) {
+    Charge.Outcome outcome = charge.getOutcome();
+    return new ChargeFailure(
+        charge.getFailureCode(),
+        charge.getFailureMessage(),
+        outcome != null ? outcome.getNetworkStatus() : null,
+        outcome != null ? outcome.getReason() : null,
+        outcome != null ? outcome.getType() : null);
+  }
+
   @Override
   public CompletableFuture<SubscriptionInformation> getSubscriptionInformation(Object subscriptionObj) {
 
@@ -497,13 +519,7 @@ public class StripeManager implements SubscriptionProcessorManager {
                 if (invoice.getChargeObject() != null) {
                   final Charge charge = invoice.getChargeObject();
                   if (charge.getFailureCode() != null || charge.getFailureMessage() != null) {
-                    Charge.Outcome outcome = charge.getOutcome();
-                    chargeFailure = new ChargeFailure(
-                        charge.getFailureCode(),
-                        charge.getFailureMessage(),
-                        outcome != null ? outcome.getNetworkStatus() : null,
-                        outcome != null ? outcome.getReason() : null,
-                        outcome != null ? outcome.getType() : null);
+                    chargeFailure = createChargeFailure(charge);
                   }
 
                   if (charge.getPaymentMethodDetails() != null
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorManager.java
index e82d6e5bc63aa9b72faf593a61c402f42c80fb28..63d391b8fec95ae2f64524cf35e7cc67622d6f1c 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorManager.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorManager.java
@@ -60,7 +60,8 @@ public interface SubscriptionProcessorManager {
   record PaymentDetails(String id,
                         Map<String, String> customMetadata,
                         PaymentStatus status,
-                        Instant created) {
+                        Instant created,
+                        @Nullable ChargeFailure chargeFailure) {
 
   }
 
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java
index 6ca617adc55016b4ef70b0ef78bf69943da3af30..4ffbed3bb72053f052e325d99ba800ecb8dcbf24 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/SubscriptionControllerTest.java
@@ -31,6 +31,7 @@ import java.time.Clock;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Base64;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -267,6 +268,47 @@ class SubscriptionControllerTest {
     assertThat(response.getStatus()).isEqualTo(422);
   }
 
+  @ParameterizedTest
+  @MethodSource
+  void createBoostReceiptPaymentRequired(final ChargeFailure chargeFailure, boolean expectChargeFailure) {
+    when(STRIPE_MANAGER.getPaymentDetails(any())).thenReturn(CompletableFuture.completedFuture(new SubscriptionProcessorManager.PaymentDetails(
+        "id",
+        Collections.emptyMap(),
+        SubscriptionProcessorManager.PaymentStatus.FAILED,
+        Instant.now(),
+        chargeFailure)
+    ));
+    Response response = RESOURCE_EXTENSION.target("/v1/subscription/boost/receipt_credentials")
+        .request()
+        .post(Entity.json("""
+            {
+              "paymentIntentId": "foo",
+              "receiptCredentialRequest": "abcd",
+              "processor": "STRIPE"
+            }
+          """));
+    assertThat(response.getStatus()).isEqualTo(402);
+
+    if (expectChargeFailure) {
+      assertThat(response.readEntity(SubscriptionController.CreateBoostReceiptCredentialsErrorResponse.class).chargeFailure()).isEqualTo(chargeFailure);
+    } else {
+      assertThat(response.readEntity(String.class)).isEqualTo("{}");
+    }
+  }
+
+  private static Stream<Arguments> createBoostReceiptPaymentRequired() {
+    return Stream.of(
+        Arguments.of(new ChargeFailure(
+            "generic_decline",
+            "some failure message",
+            null,
+            null,
+            null
+        ), true),
+        Arguments.of(null, false)
+    );
+  }
+
   @Test
   void confirmPaypalBoostProcessorError() {