Skip to content
payrocdevelopers

Handle errors

Handle transaction, communication, and device errors in your Tap to Pay on iPhone integration, and resolve a sale with an unknown outcome after a communication failure.

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:

TypeCallbackListener
Transaction errorsonError:withDescription:CoreAPISaleListener
Communication errorsonTransactionCommError:withError:withMessage:CoreAPISaleListener
Device errorsonDeviceError: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

swift
func onError(_ error: CoreError, withDescription message: String!) {
    print("Sale error \(error.rawValue): \(message ?? "")")
}

Objective-C

objectivec
- (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:

  1. Store the CoreSale object on the device.

  2. When the device is back online, use RestConnector.requestTransactionList to search for the transaction by orderId or by the last four digits of the card.

  3. Check the result:

    • code is A: we authorized the transaction.
    • code not A: 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.
  4. If we authorized the sale, we recommend that you reverse it. Create a CoreReversal with the uniqueRef of the transaction, and then send it to RestConnector.request with isRefund set to NO.

  5. (Optional) Run the sale again.

The following example checks the status of the sale and resolves it when the device is back online:

Swift

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

objectivec
- (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

swift
func onDeviceError(_ deviceError: CoreDeviceError, withDescription message: String!) {
    print("Device error \(deviceError.rawValue): \(message ?? "")")
}

Objective-C

objectivec
- (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

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

objectivec
- (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.
}

Browse guides

Search documentation

API reference169
Guides118
Knowledge38
legal1
Solutions32
Workflows74
↑↓highlight↵openView all search results

Menu

Theme

Sign out

Your saved plans remain in your organization. This browser’s private draft and account view will be cleared.

Talk to an engineer