Fix bad one-time-payment receipt creation for cancelled iDEAL.

This commit is contained in:
Alex Hart
2024-12-10 11:34:17 -04:00
committed by Greyson Parrelli
parent fa72a1788b
commit 3eea331e83
9 changed files with 245 additions and 11 deletions

View File

@@ -0,0 +1,109 @@
package org.thoughtcrime.securesms.jobs
import androidx.test.ext.junit.runners.AndroidJUnit4
import okhttp3.mockwebserver.MockResponse
import org.hamcrest.Matchers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.deleteAll
import org.signal.core.util.money.FiatMoney
import org.signal.donations.InAppPaymentType
import org.signal.donations.json.StripeIntentStatus
import org.signal.donations.json.StripePaymentIntent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatValue
import org.thoughtcrime.securesms.database.DonationReceiptTable
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assert
import org.thoughtcrime.securesms.testing.success
import org.thoughtcrime.securesms.util.TestStripePaths
import java.math.BigDecimal
import java.util.Currency
@RunWith(AndroidJUnit4::class)
class InAppPaymentAuthCheckJobTest {
companion object {
private const val TEST_INTENT_ID = "test-intent-id"
private const val TEST_CLIENT_SECRET = "test-client-secret"
}
@get:Rule
val harness = SignalActivityRule()
@Before
fun setUp() {
SignalDatabase.inAppPayments.writableDatabase.deleteAll(InAppPaymentTable.TABLE_NAME)
SignalDatabase.donationReceipts.writableDatabase.deleteAll(DonationReceiptTable.TABLE_NAME)
}
@Test
fun givenCanceledOneTimeAuthRequiredPayment_whenICheck_thenIDoNotExpectAReceipt() {
initializeMockGetPaymentIntent(status = StripeIntentStatus.CANCELED)
SignalDatabase.inAppPayments.insert(
type = InAppPaymentType.ONE_TIME_DONATION,
state = InAppPaymentTable.State.WAITING_FOR_AUTHORIZATION,
subscriberId = null,
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")).toFiatValue(),
waitForAuth = InAppPaymentData.WaitingForAuthorizationState(
stripeIntentId = TEST_INTENT_ID,
stripeClientSecret = TEST_CLIENT_SECRET
)
)
)
InAppPaymentAuthCheckJob().run()
val receipts = SignalDatabase.donationReceipts.getReceipts(InAppPaymentReceiptRecord.Type.ONE_TIME_DONATION)
receipts assert Matchers.empty()
}
@Test
fun givenSuccessfulOneTimeAuthRequiredPayment_whenICheck_thenIExpectAReceipt() {
initializeMockGetPaymentIntent(status = StripeIntentStatus.SUCCEEDED)
SignalDatabase.inAppPayments.insert(
type = InAppPaymentType.ONE_TIME_DONATION,
state = InAppPaymentTable.State.WAITING_FOR_AUTHORIZATION,
subscriberId = null,
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")).toFiatValue(),
waitForAuth = InAppPaymentData.WaitingForAuthorizationState(
stripeIntentId = TEST_INTENT_ID,
stripeClientSecret = TEST_CLIENT_SECRET
)
)
)
InAppPaymentAuthCheckJob().run()
val receipts = SignalDatabase.donationReceipts.getReceipts(InAppPaymentReceiptRecord.Type.ONE_TIME_DONATION)
receipts assert Matchers.hasSize(1)
}
private fun initializeMockGetPaymentIntent(status: StripeIntentStatus) {
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get(TestStripePaths.getPaymentIntentPath(TEST_INTENT_ID, TEST_CLIENT_SECRET)) {
MockResponse().success(
StripePaymentIntent(
id = TEST_INTENT_ID,
clientSecret = TEST_CLIENT_SECRET,
status = status,
paymentMethod = null
)
)
}
)
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import org.signal.donations.StripePaths
/**
* Stripe paths should be prefixed with 'stripe/' in order to access the proper namespacing in
* the mock server. This object serves as a convenience delegate to StripePaths.
*/
object TestStripePaths {
/**
* @see StripePaths.getPaymentIntentPath
*/
fun getPaymentIntentPath(paymentIntentId: String, clientSecret: String): String {
return withNamespace(StripePaths.getPaymentIntentPath(paymentIntentId, clientSecret))
}
/**
* @see StripePaths.getPaymentIntentConfirmationPath
*/
fun getPaymentIntentConfirmationPath(paymentIntentId: String): String {
return withNamespace(StripePaths.getPaymentIntentConfirmationPath(paymentIntentId))
}
/**
* @see StripePaths.getSetupIntentPath
*/
fun getSetupIntentPath(setupIntentId: String, clientSecret: String): String {
return withNamespace(StripePaths.getSetupIntentPath(setupIntentId, clientSecret))
}
/**
* @see StripePaths.getSetupIntentConfirmationPath
*/
fun getSetupIntentConfirmationPath(setupIntentId: String): String {
return withNamespace(StripePaths.getSetupIntentConfirmationPath(setupIntentId))
}
/**
* @see StripePaths.getPaymentIntentPath
*/
fun getPaymentMethodsPath(): String {
return withNamespace(StripePaths.getPaymentMethodsPath())
}
/**
* @see StripePaths.getTokensPath
*/
fun getTokensPath(): String {
return withNamespace(StripePaths.getTokensPath())
}
private fun withNamespace(path: String) = "stripe/$path"
}