# Virtual Terminal Settings

Configure virtual terminal behavior for your merchant account.

---

## Overview

Virtual Terminal Settings allow you to customize:

- 📋 **Field Config** - Which fields are shown and required, per payment channel
- 💳 **Card brands** - Accepted card types
- 💰 **Transaction types** - Allowed transaction types
- 💵 **Amount limits** - Maximum ticket amount
- 🌍 **Currencies** - Supported currencies
- 📝 **Dynamic descriptors** - Custom statement descriptors
- 💡 **Tips, saved cards and partial payments**

Authenticate with `Authorization: Bearer <JWT>`. Merchant users can only read and change their own merchant (`403` otherwise). Reading is open to any user of the merchant (the Virtual Terminal needs it to render); updating requires the `virtual_terminal.manage` permission.

---

## Get VT Settings

### Endpoint

```http
GET /api/v1/merchants/:merchantId/vt-settings
```

```bash
curl https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/vt-settings \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**
```json
{
  "success": true,
  "data": {
    "requiredFields": ["BillingAddress", "Email"],
    "cvvRequired": true,
    "cardBrandOptions": ["visa", "mastercard", "amex", "discover"],
    "transactionTypes": ["sale", "auth", "capture", "void", "refund"],
    "maxTicketAmount": 10000.00,
    "currencyTypes": ["USD"],
    "dynamicDescriptor": "ACME CORP",
    "displaySaveCard": false,
    "allowPartialPayments": false,
    "tipActive": false,
    "vtConfig": {
      "fields": {
        "Email": { "vt": { "required": true, "display": true } }
      },
      "paymentTypesByChannel": {
        "vt": { "card": true, "ach": false, "cash": true }
      },
      "partialPayments": false
    }
  }
}
```

---

## Update VT Settings

### Endpoint

```http
PUT /api/v1/merchants/:merchantId/vt-settings
```

Partial update: only the fields you send are changed. `vtConfig` is merged onto the stored value, so you can send just `vtConfig.fields` or just `vtConfig.paymentTypesByChannel`.

```bash
curl -X PUT https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/vt-settings \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cvvRequired": true,
    "cardBrandOptions": ["visa", "mastercard"],
    "maxTicketAmount": 5000.00,
    "tipActive": true
  }'
```

**Response:**
```json
{
  "success": true,
  "data": {
    "requiredFields": ["BillingAddress", "Email"],
    "cvvRequired": true,
    "cardBrandOptions": ["visa", "mastercard"],
    "transactionTypes": ["sale", "auth", "capture", "void", "refund"],
    "maxTicketAmount": 5000.00,
    "currencyTypes": ["USD"],
    "dynamicDescriptor": "ACME CORP",
    "displaySaveCard": false,
    "allowPartialPayments": false,
    "tipActive": true,
    "vtConfig": {
      "fields": {
        "Email": { "vt": { "required": true, "display": true } }
      },
      "paymentTypesByChannel": {
        "vt": { "card": true, "ach": false, "cash": true }
      },
      "partialPayments": false
    }
  },
  "message": "Virtual terminal settings updated successfully"
}
```

**Side effects:** tip, transaction-type and `vtConfig` changes are synced to the Virtual Terminal service. On every save, the merchant's stored Field Config and payment types (when set) are pushed down to every device of the merchant. Sync failures are logged and do not fail the request.

---

## Configuration Options

| Field | Type | Description |
|-------|------|-------------|
| `requiredFields` | string[] | Legacy list of required fields. Kept in step with `vtConfig.fields` after a Field Config save; prefer `vtConfig.fields`. |
| `cvvRequired` | boolean | CVV mandatory (default `true`) |
| `cardBrandOptions` | string[] | `visa`, `mastercard`, `amex`, `discover` |
| `transactionTypes` | string[] | Any of `sale`, `auth`, `capture`, `void`, `refund`, `cancel_capture`, `credit` |
| `maxTicketAmount` | number | Maximum ticket amount for the merchant |
| `currencyTypes` | string[] | ISO 4217 codes. Defaults to `["USD"]`. |
| `dynamicDescriptor` | string | Statement descriptor (max 255 chars) |
| `displaySaveCard` | boolean | Offer "save card" on payment forms |
| `allowPartialPayments` | boolean | Allow partial payments (also stored as `vtConfig.partialPayments`) |
| `tipActive` | boolean | Enable tip collection |
| `vtConfig` | object | Field Config and payment types, see below |

### Field Config (`vtConfig.fields`)

Per-field, per-channel display and required flags:

```json
{
  "vtConfig": {
    "fields": {
      "Email": {
        "vt": { "required": true, "display": true },
        "invoice": { "required": false, "display": true }
      },
      "BillingAddress": {
        "vt": { "required": true, "display": true }
      }
    }
  }
}
```

- **Field keys:** `CustomerName`, `Email`, `Phone`, `BillingAddress`, `ShippingAddress`, `PO`, `Description`, `Message`, `Signature`, `MakeRecurring`, `AuthTransaction`, `Customer`.
- **Channels:** `vt`, `text`, `invoice`, `web`, `qr`, `api`, `mobile`, `device`, `sdk`.
- Partial maps are accepted; the stored config is normalized to the full field × channel matrix.
- A field that an active gateway requires (for example AVS fields) cannot be made optional: the request is refused with `409 FIELD_LOCKED_BY_GATEWAY`.

### Payment Types (`vtConfig.paymentTypesByChannel`)

Which payment methods each channel offers, e.g. `{ "vt": { "card": true, "ach": true, "cash": false } }`. The legacy flat form `vtConfig.paymentTypes` (`card`, `ach`, `cash`, `crypto`) is still read by older consumers.

### Dynamic Descriptor

Customize how charges appear on customer statements:

```json
{
  "dynamicDescriptor": "ACME CORP"
}
```

Card networks typically display only the first 22–25 characters; keep it short and recognisable.

---

## Get Resolved Field Config

```http
GET /api/v1/merchants/:merchantId/field-config
```

Returns the effective Field Config for one channel — the merchant's settings merged with the requirements imposed by its active gateways.

| Query | Default | Description |
|-------|---------|-------------|
| `channel` | `vt` | One of the channels above, or `all` for every channel |
| `paymentMethod` | — | `card`, `ach` or `cash` |

An invalid `channel` or `paymentMethod` returns `400`.

---

## Terminal Preferences (Virtual Terminal Service)

Per-merchant (and optionally per-user) terminal behaviour, such as the default transaction type, receipts and OTP, is stored by the Virtual Terminal service.

### Get Terminal Preferences

```http
GET /api/v1/virtual-terminal/settings
```

### Update Terminal Preferences

```http
PUT /api/v1/virtual-terminal/settings
```

- Both endpoints sit behind the tenant's `virtual_terminal` feature (`403 SERVICE_UNAVAILABLE` when it is off). Use the `/api/v1/virtual-terminal` prefix; the gateway also routes `/api/v1/vt/*`, but the service does not serve that prefix.
- `GET` requires `virtual_terminal.access`; admins must pass `?merchantId=` (`400` otherwise). Optional `?userId=` returns that user's settings if present, otherwise the merchant's; with no saved row, defaults are returned.
- `PUT` requires `virtual_terminal.manage`. Body may include `merchantId` (required for admins) and `userId` (save a per-user row). This is **not** a partial update for the `require*` / `ttpRequire*` flags: any of them omitted from the body is saved as `false`. Accepted fields: `defaultTransactionType`, `allowSale`, `allowAuth`, `allowRefund`, `allowVoid`, `acceptCreditCard`, `acceptACH`, `acceptGiftCard`, `acceptCash`, `requireCustomer`, `requireEmail`, `requirePhone`, `requireBillingAddress`, `requireShippingAddress`, `requireOrderId`, `requireInvoiceNumber`, `requireSignature`, `signatureThreshold`, `autoEmailReceipt`, `autoPrintReceipt`, `requireOTP`, `otpMethod`, `otpThreshold`, `showConvenienceFee`, `convenienceFeeAmount`, `convenienceFeePercentage`, `allowTips`, `tipPresets`, `calculateTax`, `taxRate`, `defaultCurrency`, `customFields`, `quickAmounts`, `ttpRequireName`, `ttpRequireEmail`, `ttpRequirePhone`, `ttpRequireBillingAddress`, `ttpRequireDescription`.

> Field requirements are enforced from the merchant Field Config above, not from the `require*` flags here.

---

## Use Cases

### High-Security Merchant

```json
{
  "cvvRequired": true,
  "cardBrandOptions": ["visa", "mastercard"],
  "maxTicketAmount": 1000.00,
  "vtConfig": {
    "fields": {
      "BillingAddress": { "vt": { "required": true, "display": true } },
      "Email": { "vt": { "required": true, "display": true } },
      "Phone": { "vt": { "required": true, "display": true } }
    }
  }
}
```

### Subscription Service

```json
{
  "cvvRequired": true,
  "cardBrandOptions": ["visa", "mastercard"],
  "transactionTypes": ["sale", "refund"],
  "maxTicketAmount": 999.00,
  "displaySaveCard": true
}
```

---

## Validation Rules

The settings fields are stored as sent; the API does not validate card brand codes, transaction types, currency codes, amounts or descriptor format. Send only the documented values. The checks that do apply:

- `vtConfig.fields` that would make a gateway-required field optional → `409 FIELD_LOCKED_BY_GATEWAY`.
- A merchant user updating another merchant → `403 FORBIDDEN`.
- Missing `virtual_terminal.manage` on update → `403`.
- Unknown merchant → `404 NOT_FOUND`.

---

## Error Handling

### Field Locked by Gateway
```json
{
  "success": false,
  "error": {
    "code": "FIELD_LOCKED_BY_GATEWAY",
    "message": "These fields are required by an active gateway and cannot be made optional: BillingAddress [vt, invoice] — required by the gateway for AVS"
  }
}
```

### Merchant Not Found
```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Merchant with id 8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30 not found"
  }
}
```

---

## Best Practices

### ✅ DO:
- **Require CVV** for fraud prevention
- **Set reasonable limits** based on your business
- **Test changes** in sandbox first
- **Document settings** for your team
- **Review settings** quarterly
- **Notify customers** of accepted payment methods

### ❌ DON'T:
- **Don't disable CVV** unless absolutely necessary
- **Don't set limits too high** - increases fraud risk
- **Don't require too many fields** - reduces conversion
- **Don't change frequently** - confuses customers
- **Don't use unclear descriptors** - causes chargebacks

---

## Testing

Test your VT settings in sandbox:

```bash
# Get current settings
curl https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/vt-settings \
  -H "Authorization: Bearer YOUR_TOKEN"

# Update settings
curl -X PUT https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/vt-settings \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"maxTicketAmount": 100.00}'

# Check the resolved field config the Virtual Terminal will render
curl "https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/field-config?channel=vt" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

---

## Next Steps

- [Process transactions →](../core/transactions.md)
- [Configure AVS filters →](../fraud/avs.md)
- [Set up merchant branding →](./branding.md)

---

**Need help?** Contact support@auxcore.net
