Prerequisites: Authentication · Run a sale
The SDK reports errors through different callbacks depending on the context. There are three main types of error that the SDK can return:
| Type | Callback | Listener |
|---|---|---|
| Transaction errors | onError:withDescription: | CoreAPISaleListener |
| Communication errors | onTransactionCommError:withError:withMessage: | CoreAPISaleListener |
| Device errors | onDeviceError:withDescription: | CoreAPIDeviceListener |
Transaction errors
The SDK returns transaction processing failures, such as network problems, to onError:withDescription: on CoreAPISaleListener. The CoreError enumeration identifies the type of failure.
Swift
func onError(_ error: CoreError, withDescription message: String!) {
print("Sale error \(error.rawValue): \(message ?? "")")
}
Objective-C
- (void)onError:(CoreError)error withDescription:(NSString *)message {
NSLog(@"Sale error %d: %@", (int)error, message);
}
Note: CoreAPIRefundListener uses the same parameters for refund errors.
For the full list of CoreError values, go to the CoreError page in the SDK reference.
Communication errors
Important: If you receive a communication error, treat the sale as unresolved. If we authorized the sale, we recommend that you reverse the sale.
A communication error happens when the device reads the card successfully but the transaction request doesn't reach our gateway. Because the customer has already tapped their card, you don't know the outcome on our gateway.
The SDK returns communication errors to onTransactionCommError:withError:withMessage: on CoreAPISaleListener, and passes back the original CoreSale object.
Note: Referenced refunds don't have a dedicated communication error callback. If a referenced refund fails to reach our gateway, the SDK reports it through onError:withDescription: on CoreAPIRefundListener instead.
We recommend that you use the following recovery process for sales:
Store the
CoreSaleobject on the device.When the device is back online, use
RestConnector.requestTransactionListto search for the transaction byorderIdor by the last four digits of the card.Check the result:
codeisA: we authorized the transaction.codenotA: we declined the transaction. There's nothing to reverse.- No transaction: the transaction didn't reach our gateway. There's nothing to resolve, and you can run the sale again.
If we authorized the sale, we recommend that you reverse it. Create a
CoreReversalwith theuniqueRefof the transaction, and then send it toRestConnector.requestwithisRefundset toNO.(Optional) Run the sale again.
The following example checks the status of the sale and resolves it when the device is back online:
Swift
func onTransactionCommError(_ sale: CoreSale!, withError error: CoreError, withMessage message: String!) {
// Save the sale to disk so that it survives an app restart.
savePendingSale(sale.convertToDictionary())
}
// Call this from a background thread when the device is back online.
func resolvePendingSale(_ sale: CoreSale) {
let filter = CoreTransactionFilter()
filter.orderId = sale.orderId
filter.panLastFour = String(sale.maskedPAN.suffix(4))
do {
let result = try RestConnector.requestTransactionList(filter, with: terminal)
guard let match = result.transactionSummary?.first as? CoreTransactionSummary else {
// The transaction didn't reach our gateway. You can run the sale again.
return
}
guard match.code == "A" else {
// We declined the transaction. There's nothing to reverse.
return
}
// We authorized the transaction. Reverse it and show a receipt.
let reversal = CoreReversal()
reversal.deviceType = "AppleTTP"
reversal.uniqueRef = match.uniqueRef
try RestConnector.request(reversal, isRefund: NSNumber(value: false), with: terminal)
} catch {
// The request failed. Try again later.
}
}
Objective-C
- (void)onTransactionCommError:(CoreSale *)sale
withError:(CoreError)error
withMessage:(NSString *)message {
// Save the sale to disk so that it survives an app restart.
[self savePendingSale:[sale convertToDictionary]];
}
// Call this from a background thread when the device is back online.
- (void)resolvePendingSale:(CoreSale *)sale {
NSError *error = nil;
CoreTransactionFilter *filter = [[CoreTransactionFilter alloc] init];
filter.orderId = sale.orderId;
filter.panLastFour = [sale.maskedPAN substringFromIndex:sale.maskedPAN.length - 4];
CoreTransactions *result = [RestConnector requestTransactionList:filter
withTerminal:self.terminal
error:&error];
if (error != nil) {
// The request failed. Try again later.
return;
}
if (result.transactionSummary.count == 0) {
// The transaction didn't reach our gateway. You can run the sale again.
return;
}
CoreTransactionSummary *match = result.transactionSummary.firstObject;
if (![match.code isEqualToString:@"A"]) {
// We declined the transaction. There's nothing to reverse.
return;
}
// We authorized the transaction. Reverse it and show a receipt.
CoreReversal *reversal = [[CoreReversal alloc] init];
reversal.deviceType = @"AppleTTP";
reversal.uniqueRef = match.uniqueRef;
[RestConnector requestReversal:reversal
isRefund:[NSNumber numberWithBool:NO]
withTerminal:self.terminal
error:&error];
}
Device errors
The SDK returns device errors to onDeviceError:withDescription: on CoreAPIDeviceListener. The CoreDeviceError enumeration covers device failures, entitlement problems, and hardware faults.
Swift
func onDeviceError(_ deviceError: CoreDeviceError, withDescription message: String!) {
print("Device error \(deviceError.rawValue): \(message ?? "")")
}
Objective-C
- (void)onDeviceError:(CoreDeviceError)deviceError withDescription:(NSString *)message {
NSLog(@"Device error %d: %@", (int)deviceError, message);
}
The SDK includes a set of CoreDeviceError values that are specific to Apple Tap to Pay, and they all start with APPLE_TTP_. They cover problems such as an unsupported iPhone model, an unsupported iOS version, a disabled passcode, a blocked merchant, and account-linking failures. For the full list, go to the CoreDeviceError page in the SDK reference.
If the device encounters an error and becomes unavailable, onDeviceDisconnected: fires.
Swift
func onDeviceConnected(_ type: DeviceEnum, withDeviceInfo deviceInfo: [AnyHashable: Any]!) {
// The device is active and ready
}
func onDeviceDisconnected(_ type: DeviceEnum) {
// The reader is unavailable. Disable your payment button.
}
Objective-C
- (void)onDeviceConnected:(DeviceEnum)type withDeviceInfo:(NSDictionary *)deviceInfo {
// The device is active and ready
}
- (void)onDeviceDisconnected:(DeviceEnum)type {
// The reader is unavailable. Disable your payment button.
}