# Handle surcharges

Handle surcharges with Tap to Pay on iPhone by reading your terminal's surcharge settings, presenting the fee for confirmation, and letting the customer bypass it.

**Prerequisites:** [Authentication](/api/authentication) · [Run a sale](/guides/payments/tap-to-pay-on-iphone/run-a-sale)

A surcharge is an additional fee that we apply to card-present sales if surcharging is active on the terminal and if the card supports surcharging. When the SDK calculates a surcharge, it pauses the sale and asks your app to present the fee to the customer for confirmation.

If a card is eligible for surcharging, the following flow describes what happens during a sale:

1. Your app runs a sale.
2. The customer taps their card.
3. Our gateway authorizes the transaction and calculates the surcharge.
4. Your app presents the fee to the customer:- If the customer accepts, your app confirms that our gateway should apply the surcharge. Our gateway sends the sale for settlement.
   - If the customer declines, your app notifies the SDK, which then automatically reverses the sale.
   - If the confirmation timer expires, the SDK automatically reverses the sale.

<a id="integration-steps"></a>
## Integration steps

1. [Read your surcharge settings](#step-1-read-your-surcharge-settings).
2. [Run a sale](#step-2-run-a-sale).
3. [Confirm the surcharge](#step-3-confirm-the-surcharge).
4. [Handle reversal errors](#step-4-handle-reversal-errors)
5. [(Optional) Change the confirmation timeout](#step-5-optional-change-the-confirmation-timeout)

<a id="step-1-read-your-surcharge-settings"></a>
## Step 1. Read your surcharge settings

We configure surcharging on the terminal, so your app can't adjust the surcharge settings. Your app can only read the surcharge settings from `CoreSettings.surchargeSettings` in your `onSettingsRetrieved:` callback.

`CoreSurchargeSettings` has the following properties:

| **Property** | **Type** | **Description** |
| --- | --- | --- |
| `enable` | `NSNumber*` (bool) | `true` when surcharging is active for the terminal. |
| `allowBypass` | `NSNumber*` (bool) | `true` when customers can opt out of the surcharge before they tap. |
| `discloseFee` | `NSNumber*` (bool) | `true` when you must disclose the fee to the customer. |
| `percentage` | `NSNumber*` | The surcharge rate as a percentage of the sale amount. |

**Important:** Don't hard-code the surcharge settings. Always read `CoreSettings.surchargeSettings` at runtime.

<a id="step-2-run-a-sale"></a>
## Step 2. Run a sale

Create a sale request, and set `bypassSurcharge` to `false` or `nil`.

**Note:** If you want to let the customer bypass the surcharge, set `bypassSurcharge` to `true` instead. The SDK then skips the confirmation step in Step 3 and completes the sale without applying the surcharge fee.

**`Swift`**

```swift
guard terminal.getDevice() != NODEVICE else {
    // The reader isn't connected. Prompt the merchant to connect before you continue.
    return
}

let sale = CoreSale()
sale.amount = 42.00
sale.transactionInputMethod = TAP
sale.orderId = "YOUR_ORDER_REFERENCE"
sale.bypassSurcharge = false

terminal.processSale(sale)
```

**`Objective-C`**

```objectivec
if ([[WTPSTerminal singleton] getDevice] == NODEVICE) {
    // The reader isn't connected. Prompt the merchant to connect before you continue.
    return;
}

CoreSale *sale = [[CoreSale alloc] init];
sale.amount = [NSNumber numberWithDouble:42.00];
sale.transactionInputMethod = TAP;
sale.orderId = @"YOUR_ORDER_REFERENCE";
sale.bypassSurcharge = [NSNumber numberWithBool:NO];

[[WTPSTerminal singleton] processSale:sale];
```

<a id="step-3-confirm-the-surcharge"></a>
## Step 3. Confirm the surcharge

When you run the sale and the cardholder taps their card, our gateway authorizes the sale and checks if the card is eligible for surcharging.

After our gateway authorizes the card, the SDK calls `onRequestSurchargeConfirm:` on your `CoreAPISaleListener` with a `CoreBinLookupResponse` that contains the calculated surcharge. Depending on whether the customer accepts or declines the surcharge, your app must respond by calling `confirmSurchargeFee:` with either `true` or `false`.

**Important:** If your app doesn't call `confirmSurchargeFee:` within the timeout window, the SDK automatically reverses the sale. The default timeout window is 30 seconds. To change the timeout window, go to [Change the confirmation timeout](#step-5-optional-change-the-confirmation-timeout).

<a id="display-the-confirmation"></a>
### Display the confirmation

Use the surcharge amount from `binLookupResponse.surcharge.amount` to create a screen that displays the surcharge amount to the customer and prompts them to accept or decline the surcharge. We recommend that you show any tips or taxes alongside the surcharge so that the customer sees the full total.

<a id="if-the-customer-accepts"></a>
### If the customer accepts

Call `confirmSurchargeFee(true)`. The SDK keeps the sale as approved and calls `onSaleResponse:` with an approved response.

<a id="if-the-customer-declines"></a>
### If the customer declines

Call `confirmSurchargeFee(false)`. The SDK reverses the authorized sale. When the reversal completes, `onSaleResponse:` returns a declined response with a `code` of `D` and a `status` of `VOID`.

To handle a failed reversal, go to [Handle reversal errors](#step-4-handle-reversal-errors).

<a id="example"></a>
### Example

The following example displays the surcharge amount to the customer and calls `confirmSurchargeFee:` with the response:

**`Swift`**

```swift
func onRequestSurchargeConfirm(_ binLookupResponse: CoreBinLookupResponse!) {
    var message = String(
        format: "Surcharge fee: %@\nConfirm?",
        binLookupResponse.surcharge.amount
    )

    // Optionally include a tip and tax breakdown that you already calculated
    if let breakdown = breakDownLabel.text {
        message = breakdown + "\n" + message
    }

    showSurchargeAlert(message: message)
}

func showSurchargeAlert(message: String) {
    let alert = UIAlertController(title: "Surcharge", message: message, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Accept", style: .default) { _ in
        self.terminal.confirmSurchargeFee(true)
    })
    alert.addAction(UIAlertAction(title: "Decline", style: .destructive) { _ in
        self.terminal.confirmSurchargeFee(false)
    })
    present(alert, animated: true)
}
```

**`Objective-C`**

```objectivec
- (void)onRequestSurchargeConfirm:(CoreBinLookupResponse *)binLookupResponse {
    NSString *message = [NSString stringWithFormat:
        @"Surcharge fee: %@\nConfirm?",
        binLookupResponse.surcharge.amount];

    UIAlertController *alert = [UIAlertController
        alertControllerWithTitle:@"Surcharge"
                         message:message
                  preferredStyle:UIAlertControllerStyleAlert];

    [alert addAction:[UIAlertAction actionWithTitle:@"Accept"
                                              style:UIAlertActionStyleDefault
                                            handler:^(UIAlertAction *action) {
        [[WTPSTerminal singleton] confirmSurchargeFee:YES];
    }]];

    [alert addAction:[UIAlertAction actionWithTitle:@"Decline"
                                              style:UIAlertActionStyleDestructive
                                            handler:^(UIAlertAction *action) {
        [[WTPSTerminal singleton] confirmSurchargeFee:NO];
    }]];

    [self presentViewController:alert animated:YES completion:nil];
}
```

<a id="step-4-handle-reversal-errors"></a>
## Step 4. Handle reversal errors

If the automatic reversal fails, for example because of a network interruption, the SDK calls `onSurchargeReversalError:` with the original `CoreSaleResponse`. Our gateway authorized the sale, but the reversal didn't complete.

To handle a failed reversal, store the `uniqueRef` of the sale, and then call `terminal.processReversal` to try again. The following example handles a failed reversal:

**`Swift`**

```swift
func onSurchargeReversalError(_ saleResponse: CoreSaleResponse!) {
    guard let saleResponse = saleResponse else { return }
    // Our gateway authorized the sale but the reversal failed.
    // Store the uniqueRef and retry the reversal when the device is online.
    savePendingReversal(uniqueRef: saleResponse.uniqueRef)

    // Mark the response as declined so that you can display it
    saleResponse.code = "D"
    saleResponse.status = "VOID"
    saleResponse.descriptionCode = "SURCHARGE DECLINED"
    openReceipt(response: saleResponse)
}

// Call this when the device is back online, using the same terminal that ran the original sale.
func retryPendingReversal(uniqueRef: String) {
    let reversal = CoreReversal()
    reversal.uniqueRef = uniqueRef
    terminal.processReversal(reversal, isRefund: NSNumber(value: false))
}
```

**`Objective-C`**

```objectivec
- (void)onSurchargeReversalError:(CoreSaleResponse *)saleResponse {
    NSLog(@"onSurchargeReversalError - uniqueRef %@ requires manual reversal",
          saleResponse.uniqueRef);

    // Store the uniqueRef and retry the reversal when the device is online
    [self savePendingReversal:saleResponse.uniqueRef];

    saleResponse.code = @"D";
    saleResponse.status = @"VOID";
    saleResponse.descriptionCode = @"SURCHARGE DECLINED";
    [self openReceipt:saleResponse];
}

// Call this when the device is back online, using the same terminal that ran the original sale.
- (void)retryPendingReversal:(NSString *)uniqueRef {
    CoreReversal *reversal = [[CoreReversal alloc] init];
    reversal.uniqueRef = uniqueRef;
    [[WTPSTerminal singleton] processReversal:reversal isRefund:[NSNumber numberWithBool:NO]];
}
```

<a id="step-5-optional-change-the-confirmation-timeout"></a>
## Step 5. (Optional) Change the confirmation timeout

The SDK starts a timer when `onRequestSurchargeConfirm:` fires. If you don't call `confirmSurchargeFee:` before the timer expires, the SDK reverses the sale.

To change the timeout from the default of 30 seconds, set `surchargeConfirmationTimeoutInSeconds` in your device data dictionary before you initialize the device. The value must be greater than zero. If you set zero or a negative value, the SDK uses the default. The following example sets the timeout to 60 seconds:

**`Swift`**

```swift
var deviceSettings = Dictionary<String, Any>()
// Surcharge confirmation timeout - the default is 30 seconds
deviceSettings["surchargeConfirmationTimeoutInSeconds"] = 60

terminal.initDevice(APPLETTP, with: DCT_INTERNAL, withDeviceData: deviceSettings)
```

**`Objective-C`**

```objectivec
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
// Surcharge confirmation timeout - the default is 30 seconds
[data setObject:[NSNumber numberWithInt:60] forKey:@"surchargeConfirmationTimeoutInSeconds"];

[[WTPSTerminal singleton] initDevice:APPLETTP withConnectionType:DCT_INTERNAL withDeviceData:data];
```
