Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit a1ee78c

Browse files
authored
Merge pull request #3128 from wordpress-mobile/ipp-capture-payment-with-min-amount
[In Person Payments] Capture payment with min amount error handling
2 parents 3061745 + b04c5ae commit a1ee78c

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

example/src/androidTest/java/org/wordpress/android/fluxc/mocked/MockedStack_WCInPersonPaymentsTest.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import org.junit.Assert.assertThat
1010
import org.junit.Assert.assertTrue
1111
import org.junit.Test
1212
import org.wordpress.android.fluxc.model.SiteModel
13-
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType.CAPTURE_ERROR
13+
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType
1414
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType.MISSING_ORDER
15+
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType.CAPTURE_ERROR
1516
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType.PAYMENT_ALREADY_CAPTURED
1617
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentErrorType.SERVER_ERROR
1718
import org.wordpress.android.fluxc.model.payments.inperson.WCPaymentAccountResult.WCPaymentAccountStatus
@@ -123,6 +124,24 @@ class MockedStack_InPersonPaymentsTest : MockedStack_Base() {
123124
assertTrue(result.error?.type == SERVER_ERROR)
124125
}
125126

127+
@Test
128+
fun whenAmountTooSmallErrorThenAmountTooSmallErrorReturned() = runBlocking {
129+
interceptor.respondWithError("wc-pay-capture-terminal-payment-response-amount-too-small.json", 400)
130+
131+
val result = restClient.capturePayment(
132+
WOOCOMMERCE_PAYMENTS,
133+
testSite,
134+
DUMMY_PAYMENT_ID,
135+
-10L
136+
)
137+
138+
assertTrue(result.error!!.type == CAPTURE_ERROR)
139+
assertEquals("amount_too_small", result.error!!.extraData!!["error_type"])
140+
val extraDetails = result.error!!.extraData!!["extra_details"] as Map<*, *>
141+
assertEquals(50.0, extraDetails["minimum_amount"])
142+
assertEquals("USD", extraDetails["minimum_amount_currency"])
143+
}
144+
126145
@Test
127146
fun whenLoadAccountInvalidStatusThenFallbacksToUnknown() = runBlocking {
128147
interceptor.respondWith("wc-pay-load-account-response-new-status.json")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"code": "wcpay_capture_error",
3+
"message": "Payment capture failed to complete with the following message: Minimum required amount was not reached. The minimum amount to capture is 0.5 USD.",
4+
"data": {
5+
"status": 400,
6+
"extra_details": {
7+
"minimum_amount": 50,
8+
"minimum_amount_currency": "USD"
9+
},
10+
"error_type": "amount_too_small"
11+
}
12+
}

plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/model/payments/inperson/WCCapturePaymentResponsePayload.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class WCCapturePaymentResponsePayload(
2323

2424
class WCCapturePaymentError(
2525
val type: WCCapturePaymentErrorType = GENERIC_ERROR,
26-
val message: String = ""
26+
val message: String = "",
27+
val extraData: Map<String, Any>? = null
2728
) : OnChangedError
2829

2930
enum class WCCapturePaymentErrorType {

plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/payments/inperson/InPersonPaymentsRestClient.kt

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.wordpress.android.fluxc.network.rest.wpcom.wc.payments.inperson
22

3+
import com.android.volley.VolleyError
4+
import com.google.gson.Gson
5+
import com.google.gson.reflect.TypeToken
36
import org.wordpress.android.fluxc.generated.endpoint.WOOCOMMERCE
47
import org.wordpress.android.fluxc.model.SiteModel
58
import org.wordpress.android.fluxc.model.payments.inperson.WCCapturePaymentError
@@ -26,9 +29,13 @@ import org.wordpress.android.fluxc.store.WCInPersonPaymentsStore.InPersonPayment
2629
import org.wordpress.android.fluxc.store.WCInPersonPaymentsStore.InPersonPaymentsPluginType.STRIPE
2730
import org.wordpress.android.fluxc.store.WCInPersonPaymentsStore.InPersonPaymentsPluginType.WOOCOMMERCE_PAYMENTS
2831
import org.wordpress.android.fluxc.utils.toWooPayload
32+
import org.wordpress.android.util.AppLog
2933
import javax.inject.Inject
3034

31-
class InPersonPaymentsRestClient @Inject constructor(private val wooNetwork: WooNetwork) {
35+
class InPersonPaymentsRestClient @Inject constructor(
36+
private val wooNetwork: WooNetwork,
37+
private val gson: Gson,
38+
) {
3239
suspend fun fetchConnectionToken(
3340
activePlugin: InPersonPaymentsPluginType,
3441
site: SiteModel
@@ -71,15 +78,22 @@ class InPersonPaymentsRestClient @Inject constructor(private val wooNetwork: Woo
7178
response.data?.let { data ->
7279
WCCapturePaymentResponsePayload(site, paymentId, orderId, data.status)
7380
} ?: WCCapturePaymentResponsePayload(
74-
mapToCapturePaymentError(error = null, message = "status field is null, but isError == false"),
81+
mapToCapturePaymentError(
82+
error = null,
83+
message = "status field is null, but isError == false"
84+
),
7585
site,
7686
paymentId,
7787
orderId
7888
)
7989
}
90+
8091
is WPAPIResponse.Error -> {
8192
WCCapturePaymentResponsePayload(
82-
mapToCapturePaymentError(response.error, response.error.message ?: "Unexpected error"),
93+
mapToCapturePaymentError(
94+
response.error,
95+
response.error.message ?: "Unexpected error"
96+
),
8397
site,
8498
paymentId,
8599
orderId
@@ -146,9 +160,13 @@ class InPersonPaymentsRestClient @Inject constructor(private val wooNetwork: Woo
146160
)
147161
)
148162
}
163+
149164
is WPAPIResponse.Error -> {
150165
WCTerminalStoreLocationResult(
151-
mapToStoreLocationForSiteError(response.error, response.error.message ?: "Unexpected error")
166+
mapToStoreLocationForSiteError(
167+
response.error,
168+
response.error.message ?: "Unexpected error"
169+
)
152170
)
153171
}
154172
}
@@ -194,7 +212,10 @@ class InPersonPaymentsRestClient @Inject constructor(private val wooNetwork: Woo
194212
return response.toWooPayload()
195213
}
196214

197-
private fun mapToCapturePaymentError(error: WPAPINetworkError?, message: String): WCCapturePaymentError {
215+
private fun mapToCapturePaymentError(
216+
error: WPAPINetworkError?,
217+
message: String
218+
): WCCapturePaymentError {
198219
val type = when {
199220
error == null -> GENERIC_ERROR
200221
error.errorCode == "wcpay_missing_order" -> MISSING_ORDER
@@ -206,7 +227,23 @@ class InPersonPaymentsRestClient @Inject constructor(private val wooNetwork: Woo
206227
error.type == GenericErrorType.NETWORK_ERROR -> NETWORK_ERROR
207228
else -> GENERIC_ERROR
208229
}
209-
return WCCapturePaymentError(type, message)
230+
return WCCapturePaymentError(
231+
type = type,
232+
message = message,
233+
extraData = error?.volleyError?.getExtraData()
234+
)
235+
}
236+
237+
@Suppress("TooGenericExceptionCaught")
238+
private fun VolleyError.getExtraData(): Map<String, Any>? {
239+
val jsonString = this.networkResponse?.data?.toString(Charsets.UTF_8)
240+
return try {
241+
val mapType = object : TypeToken<Map<String, Any>>() {}.type
242+
return gson.fromJson<Map<String, Any>>(jsonString, mapType)["data"] as Map<String, Any>?
243+
} catch (e: Throwable) {
244+
AppLog.e(AppLog.T.API, "Error parsing volley error $jsonString", e)
245+
null
246+
}
210247
}
211248

212249
private fun mapToStoreLocationForSiteError(error: WPAPINetworkError?, message: String):

0 commit comments

Comments
 (0)