Skip to content

Commit c66f995

Browse files
committed
feat: improved error handling
1 parent 5cb5ccf commit c66f995

48 files changed

Lines changed: 1171 additions & 227 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DashWallet/Sources/Models/Explore Dash/Infrastructure/DAO Impl/MerchantDAO.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class MerchantDAO: PointOfUseDAO {
7272
queryFilter = queryFilter && methods.map { $0.rawValue }.contains(paymentMethodColumn)
7373
}
7474

75+
// Filter out URL-based redemption merchants (not supported)
76+
// Using literal expression to handle cases where redeemType column might not exist
77+
queryFilter = queryFilter && Expression<Bool>(literal: "(redeemType IS NULL OR redeemType != 'url')")
78+
7579
// Add denomination type filter (only applies to gift card merchants)
7680
if let denominationType = denominationType {
7781
switch denominationType {

DashWallet/Sources/Models/Explore Dash/Model/Entites/ExplorePointOfUse.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ extension ExplorePointOfUse {
5959
let deeplink: String?
6060
let savingsBasisPoints: Int // in basis points 1 = 0.001%
6161
let denominationsType: String?
62+
let redeemType: String?
6263

6364
func toSavingPercentages() -> Double {
6465
return Double(savingsBasisPoints) / 100
@@ -193,6 +194,7 @@ extension ExplorePointOfUse: RowDecodable {
193194
static let manufacturer = Expression<String?>("manufacturer")
194195
static let savingPercentage = Expression<Int>("savingsPercentage")
195196
static let denominationsType = Expression<String?>("denominationsType")
197+
static let redeemType = Expression<String?>("redeemType")
196198

197199
init(row: Row) {
198200
let name = row[ExplorePointOfUse.name]
@@ -230,8 +232,9 @@ extension ExplorePointOfUse: RowDecodable {
230232
let deeplink = row[ExplorePointOfUse.deeplink]
231233
let savingsPercentage = row[ExplorePointOfUse.savingPercentage]
232234
let denominationsType = row[ExplorePointOfUse.denominationsType]
235+
let redeemType = row[ExplorePointOfUse.redeemType]
233236
category = .merchant(Merchant(merchantId: merchantId, paymentMethod: Merchant.PaymentMethod(rawValue: paymentMethodRaw)!,
234-
type: type, deeplink: deeplink, savingsBasisPoints: savingsPercentage, denominationsType: denominationsType))
237+
type: type, deeplink: deeplink, savingsBasisPoints: savingsPercentage, denominationsType: denominationsType, redeemType: redeemType))
235238
} else if let manufacturer = try? row.get(ExplorePointOfUse.manufacturer) {
236239
let type: Atm.`Type`! = .init(rawValue: row[ExplorePointOfUse.type])
237240
category = .atm(Atm(manufacturer: manufacturer, type: type))

DashWallet/Sources/Models/Explore Dash/Services/CTXSpendAPI.swift

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ enum CTXSpendError: Error, LocalizedError {
2727
case insufficientFunds
2828
case invalidMerchant
2929
case invalidAmount
30+
case merchantUnavailable
31+
case transactionRejected
32+
case purchaseLimitExceeded
33+
case purchaseLimitBelowMinimum
34+
case serverError
3035
case customError(String)
3136
case unknown
3237
case paymentProcessingError(String)
@@ -44,11 +49,21 @@ enum CTXSpendError: Error, LocalizedError {
4449
case .tokenRefreshFailed:
4550
return NSLocalizedString("Your session expired", comment: "DashSpend")
4651
case .insufficientFunds:
47-
return NSLocalizedString("Insufficient funds to complete this purchase.", comment: "DashSpend")
52+
return NSLocalizedString("Insufficient funds to complete this purchase. Please add more Dash to your wallet or reduce the amount.", comment: "DashSpend")
4853
case .invalidMerchant:
4954
return NSLocalizedString("This merchant is currently unavailable.", comment: "DashSpend")
5055
case .invalidAmount:
5156
return NSLocalizedString("Invalid amount. Please check merchant limits.", comment: "DashSpend")
57+
case .merchantUnavailable:
58+
return NSLocalizedString("This merchant is currently unavailable. Please try again later or choose a different merchant.", comment: "DashSpend")
59+
case .transactionRejected:
60+
return NSLocalizedString("Your transaction was rejected. Please try again or contact support if the problem persists.", comment: "DashSpend")
61+
case .purchaseLimitExceeded:
62+
return NSLocalizedString("Purchase amount exceeds the maximum limit for this merchant. Please reduce the amount and try again.", comment: "DashSpend")
63+
case .purchaseLimitBelowMinimum:
64+
return NSLocalizedString("Purchase amount is below the minimum limit for this merchant. Please increase the amount and try again.", comment: "DashSpend")
65+
case .serverError:
66+
return NSLocalizedString("Server error occurred. Please try again later.", comment: "DashSpend")
5267
case .customError(let message):
5368
return message
5469
case .unknown:
@@ -88,7 +103,13 @@ final class CTXSpendAPI: HTTPClient<CTXSpendEndpoint> {
88103
if target.path.contains("/api/verify") {
89104
throw CTXSpendError.invalidCode
90105
}
91-
throw CTXSpendError.unknown
106+
throw CTXSpendError.invalidAmount
107+
} catch HTTPClientError.statusCode(let r) where r.statusCode == 409 {
108+
throw CTXSpendError.transactionRejected
109+
} catch HTTPClientError.statusCode(let r) where r.statusCode == 422 {
110+
throw CTXSpendError.invalidAmount
111+
} catch HTTPClientError.statusCode(let r) where r.statusCode >= 500 {
112+
throw CTXSpendError.serverError
92113
} catch HTTPClientError.decoder {
93114
throw CTXSpendError.parsingError
94115
}

DashWallet/Sources/Models/Explore Dash/Services/CTXSpendService.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,35 +135,48 @@ class CTXSpendService: CTXSpendAPIAccessTokenProvider, CTXSpendTokenProvider, Ob
135135
switch response.statusCode {
136136
case 400:
137137
if let errorData = try? JSONDecoder().decode(CTXSpendAPIError.self, from: response.data) {
138-
// Check for limit error first
138+
// Check for limit errors first
139139
if let fiatAmountErrors = errorData.fields?.fiatAmount,
140-
let firstFiatError = fiatAmountErrors.first,
141-
(firstFiatError == "above threshold" || firstFiatError == "below threshold") {
142-
throw CTXSpendError.customError(NSLocalizedString("The purchase limits for this merchant have changed. Please contact CTX Support for more information.", comment: "DashSpend"))
140+
let firstFiatError = fiatAmountErrors.first {
141+
if firstFiatError == "above threshold" {
142+
throw CTXSpendError.purchaseLimitExceeded
143+
} else if firstFiatError == "below threshold" {
144+
throw CTXSpendError.purchaseLimitBelowMinimum
145+
}
143146
}
144147

145148
if let firstError = errorData.errors.first {
146149
// Look for specific error messages
147150
let errorMessage = firstError.message.lowercased()
148151

149-
if errorMessage.contains("insufficient") || errorMessage.contains("funds") {
152+
if errorMessage.contains("insufficient") || errorMessage.contains("funds") || errorMessage.contains("balance") {
150153
throw CTXSpendError.insufficientFunds
154+
} else if errorMessage.contains("merchant") && (errorMessage.contains("unavailable") || errorMessage.contains("disabled") || errorMessage.contains("suspended")) {
155+
throw CTXSpendError.merchantUnavailable
151156
} else if errorMessage.contains("merchant") {
152157
throw CTXSpendError.invalidMerchant
153-
} else if errorMessage.contains("amount") || errorMessage.contains("value") {
158+
} else if errorMessage.contains("rejected") || errorMessage.contains("declined") {
159+
throw CTXSpendError.transactionRejected
160+
} else if errorMessage.contains("amount") || errorMessage.contains("value") || errorMessage.contains("limit") {
154161
throw CTXSpendError.invalidAmount
155162
}
156163

157164
// Custom error with the actual message from API
158-
throw NSError(domain: "CTXSpend", code: 400, userInfo: [NSLocalizedDescriptionKey: firstError.message])
165+
throw CTXSpendError.customError(firstError.message)
159166
}
160167
}
161168
case 401, 403:
162169
throw CTXSpendError.unauthorized
163170
case 404:
164171
throw CTXSpendError.invalidMerchant
172+
case 409:
173+
// Conflict - usually means duplicate transaction or similar
174+
throw CTXSpendError.transactionRejected
175+
case 422:
176+
// Unprocessable Entity - validation errors
177+
throw CTXSpendError.invalidAmount
165178
case 500...599:
166-
throw CTXSpendError.networkError
179+
throw CTXSpendError.serverError
167180
default:
168181
break
169182
}

DashWallet/Sources/UI/Payments/Amount/Model/Send/SendAmountModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ enum SendAmountError: Error, ColorizedText, LocalizedError {
2727
var errorDescription: String? {
2828
switch self {
2929
case .insufficientMixedFunds: return NSLocalizedString("Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction.", comment: "Send screen")
30-
case .insufficientFunds: return NSLocalizedString("Insufficient funds", comment: "Send screen")
30+
case .insufficientFunds: return NSLocalizedString("Insufficient funds. Please add more Dash to your wallet or reduce the amount.", comment: "Send screen")
3131
case .syncingChain: return NSLocalizedString("Wait until wallet is synced to complete the transaction",
3232
comment: "Send screen")
3333
}

DashWallet/ar.lproj/Localizable.strings

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,8 +1228,11 @@
12281228
/* No comment provided by engineer. */
12291229
"Insufficient funds" = "رصيد غير كاف";
12301230

1231-
/* CTXSpend error */
1232-
"Insufficient funds to complete this purchase." = "Insufficient funds to complete this purchase.";
1231+
/* DashSpend */
1232+
"Insufficient funds to complete this purchase. Please add more Dash to your wallet or reduce the amount." = "Insufficient funds to complete this purchase. Please add more Dash to your wallet or reduce the amount.";
1233+
1234+
/* Send screen */
1235+
"Insufficient funds. Please add more Dash to your wallet or reduce the amount." = "Insufficient funds. Please add more Dash to your wallet or reduce the amount.";
12331236

12341237
/* Send screen */
12351238
"Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction." = "Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction.";
@@ -1970,6 +1973,12 @@
19701973
/* Coinbase/Buy Dash */
19711974
"Purchase" = "شراء";
19721975

1976+
/* DashSpend */
1977+
"Purchase amount exceeds the maximum limit for this merchant. Please reduce the amount and try again." = "Purchase amount exceeds the maximum limit for this merchant. Please reduce the amount and try again.";
1978+
1979+
/* DashSpend */
1980+
"Purchase amount is below the minimum limit for this merchant. Please increase the amount and try again." = "Purchase amount is below the minimum limit for this merchant. Please increase the amount and try again.";
1981+
19731982
/* Alert title */
19741983
"Purchase Failed" = "Purchase Failed";
19751984

@@ -2192,10 +2201,16 @@
21922201
/* Buy Sell Dash */
21932202
"Select a service" = "Select a service";
21942203

2204+
/* DashSpend denomination selection */
2205+
"Select amount" = "Select amount";
2206+
21952207
/* Block explorer picker
21962208
Block explorer selection title */
21972209
"Select block explorer" = "Select block explorer";
21982210

2211+
/* DashSpend denomination selection subtitle */
2212+
"Select fixed amount" = "Select fixed amount";
2213+
21992214
/* No comment provided by engineer. */
22002215
"Select from Gallery" = "Select from Gallery";
22012216

@@ -2287,6 +2302,9 @@
22872302
/* No comment provided by engineer. */
22882303
"Sent to" = "أرسلت الى";
22892304

2305+
/* DashSpend */
2306+
"Server error occurred. Please try again later." = "Server error occurred. Please try again later.";
2307+
22902308
/* No comment provided by engineer. */
22912309
"Set a trusted node" = "Set a trusted node";
22922310

@@ -2479,9 +2497,6 @@
24792497
/* Coinbase */
24802498
"The minimum amount you can send is %@" = "The minimum amount you can send is %@";
24812499

2482-
/* DashSpend */
2483-
"The purchase limits for this merchant have changed. Please contact CTX Support for more information." = "The purchase limits for this merchant have changed. Please contact CTX Support for more information.";
2484-
24852500
/* Usernames */
24862501
"The username '%@' was blocked by the Dash Network. Please try again by requesting another username." = "The username '%@' was blocked by the Dash Network. Please try again by requesting another username.";
24872502

@@ -2521,6 +2536,9 @@
25212536
/* CTXSpend error */
25222537
"This merchant is currently unavailable." = "This merchant is currently unavailable.";
25232538

2539+
/* DashSpend */
2540+
"This merchant is currently unavailable. Please try again later or choose a different merchant." = "This merchant is currently unavailable. Please try again later or choose a different merchant.";
2541+
25242542
/* No comment provided by engineer. */
25252543
"This PIN will be required to unlock your app every time when you use it." = "سيكون الرقم السري هذا مطلوبًا لإلغاء قفل تطبيقك في كل مرة تستخدمه.";
25262544

@@ -3148,6 +3166,9 @@
31483166
/* DashSpend */
31493167
"Your session expired" = "Your session expired";
31503168

3169+
/* DashSpend */
3170+
"Your transaction was rejected. Please try again or contact support if the problem persists." = "Your transaction was rejected. Please try again or contact support if the problem persists.";
3171+
31513172
/* No comment provided by engineer. */
31523173
"Your transaction was sent and the amount should appear in your wallet in a few minutes." = "Your transaction was sent and the amount should appear in your wallet in a few minutes.";
31533174

DashWallet/bg.lproj/Localizable.strings

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,8 +1228,11 @@
12281228
/* No comment provided by engineer. */
12291229
"Insufficient funds" = "Недостатъчно средства";
12301230

1231-
/* CTXSpend error */
1232-
"Insufficient funds to complete this purchase." = "Insufficient funds to complete this purchase.";
1231+
/* DashSpend */
1232+
"Insufficient funds to complete this purchase. Please add more Dash to your wallet or reduce the amount." = "Insufficient funds to complete this purchase. Please add more Dash to your wallet or reduce the amount.";
1233+
1234+
/* Send screen */
1235+
"Insufficient funds. Please add more Dash to your wallet or reduce the amount." = "Insufficient funds. Please add more Dash to your wallet or reduce the amount.";
12331236

12341237
/* Send screen */
12351238
"Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction." = "Insufficient mixed funds. Wait for CoinJoin mixing to finish or disable this feature in the settings to complete this transaction.";
@@ -1970,6 +1973,12 @@
19701973
/* Coinbase/Buy Dash */
19711974
"Purchase" = "Purchase";
19721975

1976+
/* DashSpend */
1977+
"Purchase amount exceeds the maximum limit for this merchant. Please reduce the amount and try again." = "Purchase amount exceeds the maximum limit for this merchant. Please reduce the amount and try again.";
1978+
1979+
/* DashSpend */
1980+
"Purchase amount is below the minimum limit for this merchant. Please increase the amount and try again." = "Purchase amount is below the minimum limit for this merchant. Please increase the amount and try again.";
1981+
19731982
/* Alert title */
19741983
"Purchase Failed" = "Purchase Failed";
19751984

@@ -2192,10 +2201,16 @@
21922201
/* Buy Sell Dash */
21932202
"Select a service" = "Select a service";
21942203

2204+
/* DashSpend denomination selection */
2205+
"Select amount" = "Select amount";
2206+
21952207
/* Block explorer picker
21962208
Block explorer selection title */
21972209
"Select block explorer" = "Select block explorer";
21982210

2211+
/* DashSpend denomination selection subtitle */
2212+
"Select fixed amount" = "Select fixed amount";
2213+
21992214
/* No comment provided by engineer. */
22002215
"Select from Gallery" = "Select from Gallery";
22012216

@@ -2287,6 +2302,9 @@
22872302
/* No comment provided by engineer. */
22882303
"Sent to" = "Изпратени на";
22892304

2305+
/* DashSpend */
2306+
"Server error occurred. Please try again later." = "Server error occurred. Please try again later.";
2307+
22902308
/* No comment provided by engineer. */
22912309
"Set a trusted node" = "Задаване на доверен нод";
22922310

@@ -2479,9 +2497,6 @@
24792497
/* Coinbase */
24802498
"The minimum amount you can send is %@" = "The minimum amount you can send is %@";
24812499

2482-
/* DashSpend */
2483-
"The purchase limits for this merchant have changed. Please contact CTX Support for more information." = "The purchase limits for this merchant have changed. Please contact CTX Support for more information.";
2484-
24852500
/* Usernames */
24862501
"The username '%@' was blocked by the Dash Network. Please try again by requesting another username." = "The username '%@' was blocked by the Dash Network. Please try again by requesting another username.";
24872502

@@ -2521,6 +2536,9 @@
25212536
/* CTXSpend error */
25222537
"This merchant is currently unavailable." = "This merchant is currently unavailable.";
25232538

2539+
/* DashSpend */
2540+
"This merchant is currently unavailable. Please try again later or choose a different merchant." = "This merchant is currently unavailable. Please try again later or choose a different merchant.";
2541+
25242542
/* No comment provided by engineer. */
25252543
"This PIN will be required to unlock your app every time when you use it." = "Този ПИН ще бъде необходим за отключване на приложението ви всеки път, когато го използвате.";
25262544

@@ -3148,6 +3166,9 @@
31483166
/* DashSpend */
31493167
"Your session expired" = "Your session expired";
31503168

3169+
/* DashSpend */
3170+
"Your transaction was rejected. Please try again or contact support if the problem persists." = "Your transaction was rejected. Please try again or contact support if the problem persists.";
3171+
31513172
/* No comment provided by engineer. */
31523173
"Your transaction was sent and the amount should appear in your wallet in a few minutes." = "Вашата транзакция беше изпратена и сумата би трябвало да се появи във вашият портфейл до няколко минути.";
31533174

0 commit comments

Comments
 (0)