Skip to content

Commit

Permalink
Completed partial payments for iOS and improved error handling for AP…
Browse files Browse the repository at this point in the history
…I callbacks
  • Loading branch information
Robert-SD committed Nov 19, 2024
1 parent 92f9217 commit 5d3dafd
Show file tree
Hide file tree
Showing 17 changed files with 2,722 additions and 2,395 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ data class DropInConfigurationDTO (
val skipListWhenSinglePaymentMethod: Boolean,
val isRemoveStoredPaymentMethodEnabled: Boolean,
val preselectedPaymentMethodTitle: String? = null,
val paymentMethodNames: Map<String?, String?>? = null

val paymentMethodNames: Map<String?, String?>? = null,
val isPartialPaymentSupported: Boolean
) {
companion object {
@Suppress("UNCHECKED_CAST")
Expand Down Expand Up @@ -346,7 +346,8 @@ data class DropInConfigurationDTO (
val isRemoveStoredPaymentMethodEnabled = list[12] as Boolean
val preselectedPaymentMethodTitle = list[13] as String?
val paymentMethodNames = list[14] as Map<String?, String?>?
return DropInConfigurationDTO(environment, clientKey, countryCode, amount, shopperLocale, cardConfigurationDTO, applePayConfigurationDTO, googlePayConfigurationDTO, cashAppPayConfigurationDTO, analyticsOptionsDTO, showPreselectedStoredPaymentMethod, skipListWhenSinglePaymentMethod, isRemoveStoredPaymentMethodEnabled, preselectedPaymentMethodTitle, paymentMethodNames)
val isPartialPaymentSupported = list[15] as Boolean
return DropInConfigurationDTO(environment, clientKey, countryCode, amount, shopperLocale, cardConfigurationDTO, applePayConfigurationDTO, googlePayConfigurationDTO, cashAppPayConfigurationDTO, analyticsOptionsDTO, showPreselectedStoredPaymentMethod, skipListWhenSinglePaymentMethod, isRemoveStoredPaymentMethodEnabled, preselectedPaymentMethodTitle, paymentMethodNames, isPartialPaymentSupported)
}
}
fun toList(): List<Any?> {
Expand All @@ -366,6 +367,7 @@ data class DropInConfigurationDTO (
isRemoveStoredPaymentMethodEnabled,
preselectedPaymentMethodTitle,
paymentMethodNames,
isPartialPaymentSupported,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,17 @@ class DropInPlatformApi(
PaymentMethodsApiResponse.SERIALIZER.deserialize(
JSONObject(paymentMethodsResponse),
)
val paymentMethodsWithoutGiftCards = removeGiftCardPaymentMethods(paymentMethodsApiResponse)
val paymentMethodsWithoutGiftCards =
removeGiftCardPaymentMethods(
paymentMethodsApiResponse,
dropInConfigurationDTO.isPartialPaymentSupported
)
val dropInConfiguration = dropInConfigurationDTO.mapToDropInConfiguration(activity.applicationContext)
withContext(Dispatchers.Main) {
DropIn.startPayment(
activity.applicationContext,
dropInAdvancedFlowLauncher,
paymentMethodsApiResponse,
paymentMethodsWithoutGiftCards,
dropInConfiguration,
AdvancedDropInService::class.java,
)
Expand Down Expand Up @@ -269,10 +273,14 @@ class DropInPlatformApi(
)
}

// Gift cards will be supported in a later version
private fun removeGiftCardPaymentMethods(
paymentMethodsResponse: PaymentMethodsApiResponse
paymentMethodsResponse: PaymentMethodsApiResponse,
isPartialPaymentSupported: Boolean
): PaymentMethodsApiResponse {
if (isPartialPaymentSupported) {
return paymentMethodsResponse
}

val giftCardTypeIdentifier = "giftcard"
val storedPaymentMethods =
paymentMethodsResponse.storedPaymentMethods?.filterNot { it.type == giftCardTypeIdentifier }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ class AdvancedDropInService : DropInService(), LifecycleOwner {
shouldUpdatePaymentMethods: Boolean
) {
setOrderCancelObserver()
val event = JSONObject()
event.put("order", Order.SERIALIZER.serialize(order))
event.put("shouldUpdatePaymentMethods", shouldUpdatePaymentMethods)
DropInOrderCancelPlatformMessenger.sendResult(event)
val cancelOrderData = JSONObject()
cancelOrderData.put(Constants.ORDER_KEY, Order.SERIALIZER.serialize(order))
cancelOrderData.put(Constants.SHOULD_UPDATE_PAYMENT_METHODS_KEY, shouldUpdatePaymentMethods)
DropInOrderCancelPlatformMessenger.sendResult(cancelOrderData)
}

override fun onRemoveStoredPaymentMethod(storedPaymentMethod: StoredPaymentMethod) {
Expand Down Expand Up @@ -246,8 +246,9 @@ class AdvancedDropInService : DropInService(), LifecycleOwner {
)
} else {
val updatedPaymentMethodsJSON =
JSONObject(paymentEventDTO.data["updatedPaymentMethods"] as HashMap<*, *>)
val orderResponseJSON = JSONObject(paymentEventDTO.data["orderResponse"] as HashMap<*, *>)
JSONObject(paymentEventDTO.data[Constants.UPDATED_PAYMENT_METHODS_KEY] as HashMap<*, *>)
val orderResponseJSON =
JSONObject(paymentEventDTO.data[Constants.ORDER_RESPONSE_KEY] as HashMap<*, *>)
val paymentMethods = PaymentMethodsApiResponse.SERIALIZER.deserialize(updatedPaymentMethodsJSON)
val orderResponse = OrderResponse.SERIALIZER.deserialize(orderResponseJSON)
DropInServiceResult.Update(paymentMethods, orderResponse)
Expand All @@ -272,63 +273,90 @@ class AdvancedDropInService : DropInService(), LifecycleOwner {
}

private fun mapToBalanceDropInServiceResult(response: String): BalanceDropInServiceResult {
if (response.isEmpty()) {
return BalanceDropInServiceResult.Error(errorDialog = null, reason = "Balance check failed")
}
try {
val jsonResponse = JSONObject(response)
return when (val resultCode = jsonResponse.optString(Constants.RESULT_CODE_KEY)) {
"Success" -> BalanceDropInServiceResult.Balance(BalanceResult.SERIALIZER.deserialize(jsonResponse))
"NotEnoughBalance" ->
BalanceDropInServiceResult.Balance(
BalanceResult.SERIALIZER.deserialize(
jsonResponse
)
)

val jsonResponse = JSONObject(response)
return when (val resultCode = jsonResponse.optString("resultCode")) {
"Success" -> BalanceDropInServiceResult.Balance(BalanceResult.SERIALIZER.deserialize(jsonResponse))
"NotEnoughBalance" -> BalanceDropInServiceResult.Balance(BalanceResult.SERIALIZER.deserialize(jsonResponse))
else ->
BalanceDropInServiceResult.Error(
errorDialog = ErrorDialog(message = resultCode),
dismissDropIn = false,
)
else ->
BalanceDropInServiceResult.Error(
errorDialog =
ErrorDialog(
title = resultCode,
message = jsonResponse.optString(Constants.MESSAGE_KEY) ?: "Unknown"
),
dismissDropIn = false
)
}
} catch (exception: Exception) {
return BalanceDropInServiceResult.Error(
errorDialog = null,
reason = "Failure parsing balance check response."
)
}
}

private fun mapToOrderDropInServiceResult(response: String): OrderDropInServiceResult {
if (response.isEmpty()) {
return OrderDropInServiceResult.Error(errorDialog = null, reason = "Order request failed")
}

val jsonResponse = JSONObject(response)
return when (val resultCode = jsonResponse.optString("resultCode")) {
"Success" -> OrderDropInServiceResult.OrderCreated(OrderResponse.SERIALIZER.deserialize(jsonResponse))
else ->
OrderDropInServiceResult.Error(
errorDialog = ErrorDialog(message = resultCode),
dismissDropIn = false,
)
try {
val jsonResponse = JSONObject(response)
return when (val resultCode = jsonResponse.optString(Constants.RESULT_CODE_KEY)) {
"Success" -> OrderDropInServiceResult.OrderCreated(OrderResponse.SERIALIZER.deserialize(jsonResponse))
else ->
OrderDropInServiceResult.Error(
errorDialog =
ErrorDialog(
title = resultCode,
message = jsonResponse.optString(Constants.MESSAGE_KEY) ?: "Unknown"
),
dismissDropIn = false
)
}
} catch (exception: Exception) {
return OrderDropInServiceResult.Error(
errorDialog = null,
reason = "Failure parsing order response."
)
}
}

private fun mapToOrderCancelDropInServiceResult(
orderCancelResponseDTO: OrderCancelResponseDTO?
): DropInServiceResult? {
if (orderCancelResponseDTO == null || orderCancelResponseDTO.orderCancelResponseBody.isEmpty()) {
return DropInServiceResult.Error(errorDialog = null, reason = "Order cancellation failed")
}

val orderCancelResponseBody = JSONObject(orderCancelResponseDTO.orderCancelResponseBody)
return when (val resultCode = orderCancelResponseBody.optString("resultCode")) {
"Received" -> {
if (orderCancelResponseDTO.updatedPaymentMethods?.isNotEmpty() == true) {
val updatedPaymentMethods = orderCancelResponseDTO.updatedPaymentMethods
val paymentMethods =
PaymentMethodsApiResponse.SERIALIZER.deserialize(JSONObject(updatedPaymentMethods))
val orderResponse = OrderResponse.SERIALIZER.deserialize(orderCancelResponseBody)
sendResult(DropInServiceResult.Update(paymentMethods, orderResponse))
try {
val orderCancelResponseBody = orderCancelResponseDTO?.orderCancelResponseBody?.let { JSONObject(it) }
return when (val resultCode = orderCancelResponseBody?.optString(Constants.RESULT_CODE_KEY)) {
"Received" -> {
if (orderCancelResponseDTO.updatedPaymentMethods?.isNotEmpty() == true) {
val updatedPaymentMethods = orderCancelResponseDTO.updatedPaymentMethods
val paymentMethods =
PaymentMethodsApiResponse.SERIALIZER.deserialize(JSONObject(updatedPaymentMethods))
val orderResponse = OrderResponse.SERIALIZER.deserialize(orderCancelResponseBody)
sendResult(DropInServiceResult.Update(paymentMethods, orderResponse))
}
null
}
null
}

else ->
DropInServiceResult.Error(
errorDialog = ErrorDialog(message = resultCode),
dismissDropIn = false,
)
else ->
DropInServiceResult.Error(
errorDialog =
ErrorDialog(
title = resultCode,
message = orderCancelResponseBody?.optString(Constants.MESSAGE_KEY) ?: "Unknown"
),
dismissDropIn = false,
)
}
} catch (exception: Exception) {
return DropInServiceResult.Error(
errorDialog = null,
reason = "Failure parsing order cancellation response."
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,11 @@ class Constants {
const val SDK_PAYMENT_CANCELED_IDENTIFIER = "Payment canceled"
const val ADVANCED_PAYMENT_DATA_KEY = "data"
const val ADVANCED_EXTRA_DATA_KEY = "extra"
const val ORDER_KEY = "order"
const val ORDER_RESPONSE_KEY = "orderResponse"
const val SHOULD_UPDATE_PAYMENT_METHODS_KEY = "shouldUpdatePaymentMethods"
const val UPDATED_PAYMENT_METHODS_KEY = "updatedPaymentMethods"
const val RESULT_CODE_KEY = "resultCode"
const val MESSAGE_KEY = "message"
}
}
3 changes: 2 additions & 1 deletion example/lib/network/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ class Service {
return jsonDecode(response.body);
}

Future<Map<String, dynamic>> postOrdersCancel(Map<String, dynamic> body) async {
Future<Map<String, dynamic>> postOrdersCancel(
Map<String, dynamic> body) async {
final response = await http.post(
Uri.https(Config.baseUrl, "/${Config.apiVersion}/orders/cancel"),
headers: _createHeaders(),
Expand Down
3 changes: 1 addition & 2 deletions example/lib/utils/payment_event_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ class PaymentEventHandler {

bool _isNonFullyPaidOrder(jsonResponse) {
if (jsonResponse.containsKey("order")) {
final remainingAmount =
jsonResponse["order"]["remainingAmount"]["value"];
final remainingAmount = jsonResponse["order"]["remainingAmount"]["value"];
return remainingAmount > 0;
} else {
return false;
Expand Down
Loading

0 comments on commit 5d3dafd

Please sign in to comment.