TransIT Integration
1. Overview
This document specifies the monorepo's TSYS TransIT integration via the shared Kotlin library libs/transit-client.
libs/transit-clientis the canonical API wrapper for TransIT request/response models, wrappers, retries, and exception handling.services/processingintegrates throughTransitPaymentProcessor, which maps service DTOs to transit-client DTOs and delegates toTransitClient.- Other services can also use the same library for non-processing workflows (for example merchant activation and provisioning).
2. TransIT API Details
Environments
| Environment | Base URL | Purpose |
|---|---|---|
| Sandbox | https://stagegw.transnox.com/servlets/TransNox_API_Server | Development, staging, and certification testing |
| Production | https://gateway.transit-pass.com/servlets/TransNox_API_Server | Live transactions |
Runtime services read the active endpoint from TRANSIT_BASE_URL. Keep
environment values aligned with the TransIT Developer Portal and the merchant
account profile before changing staging or production routing.
Authentication
Every request to TransIT requires:
Headers:
Content-Type: application/json
Accept: application/json
Body (in every request):
"deviceID": "<assigned by TransIT>",
"transactionKey": "<assigned by TransIT>",
"operatorID": "<our operator identifier>"
- deviceID: Unique identifier for our integration endpoint (assigned during onboarding)
- transactionKey: Secret key for authenticating requests (assigned during onboarding)
- operatorID: Identifies the operator performing the transaction (configurable per-merchant)
Credential profile records and merchant-onboarding activation flows are the
source of truth for merchant deviceID and transactionKey values. Do not put
raw TransIT credentials in support docs, PR descriptions, or runbook examples.
API Version
- TransIT publishes versioned API revisions through the Developer Portal.
- Gateway request/response compatibility lives in
libs/transit-clientmodels and certification fixtures. - Format: JSON. XML may be supported by TransIT, but Gateway serializes TransIT traffic as JSON.
Integration Architecture (Current Code)
services/* -> libs/transit-client: TransitClient -> TransIT endpoint
TransitClientcentralizes wrapper selection (Sale,Auth, etc.), request body assembly, credentials injection, retries, and response/decline parsing.- Transport concerns stay inside
libs/transit-client(OkHttpClient, timeout/backoff, sensitive field redaction). - Service modules own business mapping:
services/processing: transaction workflows throughTransitPaymentProcessorservices/management: merchant/activation related integration pointsservices/merchant-onboarding: activation and provisioning retry orchestration
- Exception boundary is split by layer:
TransitClientthrowsTransitException.*- callers map those to service-level outcomes/exceptions
3. TransIT Client API Surface (Current)
The table below reflects the current operations in libs/transit-client/src/main/kotlin/com/myriad/gateway/transit/TransitClient.kt.
3.1 Core Transaction and Utility Operations (Existing)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| Sale | Sale | transactionAmount, cardNumber/tokenID, cardDataSource, transactionIndustryType, externalReferenceID | sale(credentials, SaleRequest) |
| Auth | Auth | transactionAmount, cardNumber/tokenID, cardDataSource, transactionIndustryType, externalReferenceID | authorize(credentials, AuthRequest) |
| Capture | Capture | transactionID, transactionAmount, externalReferenceID | capture(credentials, CaptureRequest) |
| Void | Void | transactionID, externalReferenceID | void(credentials, VoidRequest) |
| Return / Refund | Return | transactionID or card fields, transactionAmount, externalReferenceID | refund(credentials, ReturnRequest) |
| TipAdjust | TipAdjustment | tip, transactionID, externalReferenceID | tipAdjust(credentials, TipAdjustRequest) |
| L2L3Adjust | TransactionAdjustment | transactionID, level2Data, level3Data, externalReferenceID | l2l3Adjust(credentials, L2L3AdjustRequest) |
| CardVerification | CardVerification | cardNumber, expirationDate, CVV2, externalReferenceID | cardVerification(credentials, CardVerificationRequest) |
| BalanceInquiry | BalanceInquiry | card/account lookup fields + externalReferenceID | balanceInquiry(credentials, BalanceInquiryRequest) |
| BatchClose | BatchClose | optional batch context + externalReferenceID | batchClose(credentials, BatchCloseRequest) |
| MerchantActivation | ActivateMerchant | merchant onboarding fields | merchantActivation(credentials, MerchantActivationRequest) |
| ForcedAuth | ForcedAuth | auth fields + force-auth details | forcedAuth(credentials, ForcedAuthRequest) |
| CardAuthentication | CardAuthentication | 3DS/card-auth fields | cardAuthentication(credentials, CardAuthenticationRequest) |
| DebitSale | DebitSale | debit card/PIN transaction fields | debitSale(credentials, DebitSaleRequest) |
| DebitReturn | DebitReturn | debit refund fields | debitReturn(credentials, DebitReturnRequest) |
| CashSale | CashSale | cash sale amount/order fields | cashSale(credentials, CashSaleRequest) |
| Notification | Notification | callback/event payload fields | notification(credentials, NotificationRequest) |
| GenerateReceipt | GenerateReceipt | transactionID, receipt delivery fields | generateReceipt(credentials, GenerateReceiptRequest) |
| SearchTransaction | SearchTransaction | search criteria (transactionID, refs, date ranges) | searchTransaction(credentials, SearchTransactionRequest) |
| GenerateKey | GenerateKey | key generation payload (no merchant credentials wrapper) | generateKey(GenerateKeyRequest) |
3.2 Newer Operations
Network Tokenization (4)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| GenerateToken | GenerateToken | PAN/tokenization input + customer context | generateToken(credentials, GenerateTokenRequest) |
| GenerateTokenCryptogram | GenerateTokenCryptogram | token + cryptogram context (amount, auth context) | generateTokenCryptogram(credentials, GenerateTokenCryptogramRequest) |
| GetTokenStatus | GetTokenStatus | token identifier/status lookup fields | getTokenStatus(credentials, GetTokenStatusRequest) |
| GetTokenCardMetaData | GetTokenCardMetaData | token identifier for metadata retrieval | getTokenCardMetadata(credentials, GetTokenCardMetaDataRequest) |
EBT (5)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| EbtCashBenefitPurchase | EbtCashBenefitPurchase | EBT account + purchase amount fields | ebtCashBenefitPurchase(credentials, EbtCashBenefitPurchaseRequest) |
| EbtCashWithdrawal | EbtCashWithdrawal | EBT account + withdrawal amount fields | ebtCashWithdrawal(credentials, EbtCashWithdrawalRequest) |
| EbtElectronicVoucher | EbtElectronicVoucher | voucher ID + voucher redemption fields | ebtElectronicVoucher(credentials, EbtElectronicVoucherRequest) |
| EbtFoodStampPurchase | EbtFoodStampPurchase | food-stamp purchase amount/account fields | ebtFoodStampPurchase(credentials, EbtFoodStampPurchaseRequest) |
| EbtReturn | EbtReturn | original EBT transaction reference + return amount | ebtReturn(credentials, EbtReturnRequest) |
eInvoice (7)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| CreateInvoice | CreateInvoice | invoice amount, recipient, due and metadata fields | createInvoice(credentials, CreateInvoiceRequest) |
| CancelInvoice | CancelInvoice | invoice identifier + cancel reason/context | cancelInvoice(credentials, CancelInvoiceRequest) |
| GenerateInvoiceEmail | GenerateInvoiceEmail | invoice identifier + recipient email context | generateInvoiceEmail(credentials, GenerateInvoiceEmailRequest) |
| InvoiceAch | InvoiceAch | invoice identifier + ACH payment details | invoiceAch(credentials, InvoiceAchRequest) |
| InvoiceSale | InvoiceSale | invoice identifier + card payment details | invoiceSale(credentials, InvoiceSaleRequest) |
| SearchInvoice | SearchInvoice | invoice search criteria (ID, merchant/customer/date status) | searchInvoice(credentials, SearchInvoiceRequest) |
| UpdateInvoice | UpdateInvoice | invoice identifier + mutable invoice fields | updateInvoice(credentials, UpdateInvoiceRequest) |
Digital Wallet (4)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| DigitalWalletSale | DigitalWalletSale | wallet payload + sale amount/context | digitalWalletSale(credentials, DigitalWalletSaleRequest) |
| DigitalWalletAuth | DigitalWalletAuth | wallet payload + auth amount/context | digitalWalletAuth(credentials, DigitalWalletAuthRequest) |
| SecureAuth | SecureAuth | secure wallet auth payload (network token + cryptogram) | secureAuth(credentials, SecureAuthRequest) |
| SecureSale | SecureSale | secure wallet sale payload (network token + cryptogram) | secureSale(credentials, SecureSaleRequest) |
ACH (4)
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| AchSale | Ach | routing/account details, amount, SEC context | achSale(credentials, AchSaleRequest) |
| AchReturn | AchReturn | original ACH transaction reference + return reason | achReturn(credentials, AchReturnRequest) |
| AchResubmit | AchResubmit | original ACH reference + resubmission details | achResubmit(credentials, AchResubmitRequest) |
| AchVerification | AchVerification | account verification (routing/account/customer fields) | achVerification(credentials, AchVerificationRequest) |
Signature and EMV Enrichment
| Operation | Request Wrapper | Key Fields | Kotlin Method |
|---|---|---|---|
| UpdateSignature | UpdateTransactionSignature | transactionID, signature payload (signatureData/image fields) | updateSignature(credentials, UpdateSignatureRequest) |
| EMV Enrichment | TransactionAdjustment plus EMV fields on sale/auth requests | L2/L3 line items (level2Data, level3Data) and EMV request fields (emvTags, terminal/card-present capability fields) | l2l3Adjust(credentials, L2L3AdjustRequest) and EMV-capable sale/authorize models |
3.3 Processing Service Delegation
services/processing/src/main/kotlin/com/myriad/gateway/processing/service/TransitPaymentProcessor.kt is the adapter from service DTOs to transit-client models:
- injects
TransitClientas a constructor dependency (@Component) - maps
SaleRequest,AuthRequest, capture/void/refund/tip/enrichment inputs into transit-client request DTOs - delegates to
sale,authorize,capture,void,refund,tipAdjust,l2l3Adjust, andbatchClose - maps transit responses back to
TransactionResult - maps
TransitExceptiontypes to service-layer behavior (declines -> declined result, auth/network/server errors -> service exceptions)
4. Transaction Industry Types
| Code | Type | Use Case |
|---|---|---|
RE | Retail | PeakPOS in-store transactions |
EC | E-Commerce | Online payment website |
RS | Restaurant | Derived for restaurant MCCs such as 5812-5814 |
HC | Healthcare | Derived for healthcare MCCs such as 8011 |
DM | Direct Marketing | Mail/phone orders, actively used now |
LD | Lodging | Derived for lodging MCCs such as 7011 and 3500-3999 |
5. Card Data Sources
| Code | Description | Our Usage |
|---|---|---|
MANUAL | Manually keyed card number | Management portal manual entry, online payments |
SWIPE | Magnetic stripe | Pre-certified terminal |
EMV | Chip insertion | Pre-certified terminal |
CONTACTLESS | NFC tap | Pre-certified terminal |
TOKEN | Previously tokenized card | Recurring payments, stored cards |
INTERNET | E-commerce transaction | Online payment website |
5.1 SoftPOS / Tap on Phone Mapping Gap
The downloaded Visa, Mastercard, Amex, Apple, and PCI MPoC materials define network-level requirements that are only partially mapped to the local TransIT server-to-server materials. Do not implement production SoftPOS authorization mapping until TSYS confirms the exact host fields for software-based contactless acceptance.
Public TSYS operating-guide pages confirm the merchant-processing rules surface, but they are not enough to derive the TransIT Multipass JSON request shape for SoftPOS. Authenticated TransIT developer portal / partner docs are available and are the primary next source for implementation details before escalating to the TSYS account team.
Known inputs from current docs:
- TransIT already supports card-present EMV/contactless flows for pre-certified terminals and returns EMV metadata such as
emvTags[],emvAppLabel,cardDataEntry, and host transaction identifiers. - The local
emv_samples.jsonfile labels sale/auth/debit examples as NFC / EMV / EMVContactless, but submits those requests withcardDataSource = EMV,emvTags.tag[], terminal capability fields, card-present/cardholder-present fields, and host response fields for issuer scripts and issuer authentication data. - The same EMV sample set shows online PIN transport fields (
pin,pinKsn) on a debit EMVContactless example. This is useful evidence for the field names, but it does not prove SoftPOS PIN block format, DUKPT domain, or host key requirements. - The local
digital_wallet_samples.jsonfile covers card-not-present SecureAuth/SecureSale wallet-token flows usingINTERNETorPHONEsources plus wallet payloads. Those examples are not evidence for card-present Tap to Phone or Apple Tap to Pay on iPhone submission. - The local TransIT v6.2 certification workbook covers ecommerce, direct marketing / MOTO, recurring/installment, non-SDK in-app, and merchant activation scenarios. It does not appear to include a dedicated SoftPOS, Tap to Phone, or card-present EMV host-cert script.
- Visa Tap to Phone documentation says the authorization and clearing path must identify Tap to Phone / mobile acceptance. For Base I/SMS authorization, the source material calls out terminal type
9, terminal entry capability8, and Field 55 supplemental Form Factor Indicator tag9F6Ebyte 4 bit 8 set. For clearing/settlement it calls out corresponding mobile/proximity indicators. - Mastercard Tap on Phone documentation requires the acquirer/processor path to identify the acceptance channel as Tap on Phone; the implementation guide specifically calls out
PDS 0023 = CT9. - Amex contactless documentation requires contactless identification in Point of Service Data Code position 6 using
X. - The Amex SoftPOS kernel returns outcome, message, Field 55, transaction tag data, and online PIN indicators that must be preserved through gateway processing and certification evidence.
- Apple Tap to Pay on iPhone produces an encrypted card-read result in the app, then the PSP backend calls Apple's Proximity Payment Service, validates Apple's signed response, unwraps data through the PSP HSM, and receives cardholder / EMV tag data plus reader and transaction identifiers that must be logged for troubleshooting.
- The local TransIT sample set includes EMV terminal fields and online PIN fields (
pin,pinKsn) for device-originated debit flows, but does not prove the SoftPOS request shape for direct gateway submission.
Open TSYS / online-doc confirmation items:
- Whether SoftPOS contactless sale/auth requests should use
cardDataSource = EMV,CONTACTLESS, or another value. Current local sample evidence points toEMVfor EMVContactless samples, while the support-doc source table still listsCONTACTLESSfor NFC taps. - Which terminal/card-present capability fields must be populated for iOS and Android SoftPOS.
- Where Visa terminal type
9, Visa terminal entry capability8, Visa Form Factor Indicator tag9F6E, MastercardCT9, Amex contactless indicatorX, CDCVM state, kernel outcome, and contactless reader capability flags are carried in TransIT / TSYS messages. - Exact Field 55/tag format, allowed tag list, and whether raw kernel tag maps must be transformed or filtered before submission.
- Online PIN support path for SoftPOS, including PIN block format, KSN handling, debit routing indicators, and whether host-side keys differ from pre-certified terminal keys.
- Whether Apple Tap to Pay on iPhone decrypted output and Android SoftPOS device-token / kernel-output flows use SecureAuth/SecureSale models, card-present EMV models, or a distinct TransIT operation.
- Whether Apple
readerIdentifierandPaymentCardReadResult.id/ transaction identifier should be carried to TransIT, stored only in gateway evidence, or both. - Whether the online TransIT certification packet has a newer SoftPOS / Tap to Phone host-cert script than the local v6.2 workbook.
Until these items are answered, the processing service should persist full kernel outputs for diagnostics and certification evidence, but keep SoftPOS host-submission mapping behind a disabled capability flag.
5.2 TSYS SoftPOS Question Packet
Use this first against the authenticated TransIT online docs / developer portal. Anything not answered there becomes the outbound checklist for the TSYS account / certification team. The answer should be specific enough to implement TransitPaymentProcessor mapping, write test fixtures, and prepare Visa / Mastercard / Amex certification evidence.
| Area | Questions | Why it blocks implementation |
|---|---|---|
| Operation / wrapper | Should Android SoftPOS and Apple Tap to Pay on iPhone use Sale / Auth / DebitSale, SecureAuth / SecureSale, or a separate TransIT operation? | Determines transit-client DTOs, processing service routing, and certification fixtures. |
| Card data source | For contactless SoftPOS, should cardDataSource be EMV, CONTACTLESS, or another value? Does the answer differ for iOS Apple Tap to Pay output? | Local EMVContactless samples use EMV, but network Tap to Phone indicators still need explicit host support. |
| EMV tag transport | What tag container should carry Field 55 / kernel output? Which tags are required, optional, filtered, or transformed by TSYS? | SDK-05 metadata and NET-07 evidence need to preserve exactly what the host expects. |
| Visa indicators | Where do Visa terminal type 9, terminal entry capability 8, and Form Factor Indicator tag 9F6E byte 4 bit 8 land in TransIT requests and clearing? | Required for Visa Tap to Phone transaction coding and L3 evidence. |
| Mastercard indicators | Where is Mastercard Tap on Phone PDS 0023 = CT9 represented in the TransIT request / host message? | Required for Mastercard M-TIP and Tap on Phone acceptance identification. |
| Amex indicators | Where is Amex contactless Point of Service Data Code position 6 value X represented? | Required for Amex contactless / SoftPOS L3 evidence. |
| Terminal capability fields | Which TransIT terminal/card-present fields are mandatory for software acceptance on Android and iOS? | Prevents cert failures caused by defaulting traditional terminal values. |
| Online PIN | Are pin and pinKsn the correct fields for SoftPOS Online PIN? Which PIN block format, KSN format, key domain, and debit-routing fields are required? | Determines whether Online PIN is supported for SoftPOS and how MPoC evidence expands. |
| Apple identifiers | Should Apple readerIdentifier, PaymentCardReadResult.id, PSP transaction ID, or Proximity Payment Service response IDs be submitted to TransIT, retained only by gateway, or both? | Needed for dispute support, Apple troubleshooting, and certification trace correlation. |
| Certification script | Is there a TransIT SoftPOS / Tap to Phone host-cert script separate from the current v6.2 ecommerce / MOTO / in-app workbook? | The local workbook does not include SoftPOS card-present scenarios. |
| Environment setup | Which test credentials, device IDs, terminal profiles, BIN routing, and merchant category settings are needed for Visa, Mastercard, and Amex SoftPOS certification? | Blocks end-to-end test runs and evidence collection. |
| Receipts / response fields | Which response fields should the gateway persist for issuer scripts, issuer authentication data, receipt text, card entry mode, and network transaction identifiers? | Required for issuer-script completion, audit, and support traceability. |
6. Response Codes
Approval Codes (Prefix: A)
| Code | Meaning |
|---|---|
A0000 | Approved |
A0001 | Approved (partial) |
Decline Codes (Prefix: D)
| Code | Meaning |
|---|---|
D0092 | Declined, general |
D1218 | Declined, invalid card |
D1219 | Declined, expired card |
D1220 | Declined, insufficient funds |
D1221 | Declined, CVV mismatch |
D1222 | Declined, AVS mismatch |
D1223 | Declined, card restricted |
D1224 | Declined, do not honor |
D1225 | Declined, pickup card |
D1226 | Declined, invalid transaction |
Error Codes (Prefix: E)
| Code | Meaning |
|---|---|
E0913 | Error, invalid device credentials |
E0XXX | Error, communication failure |
NOTE: Full response code table available in TransIT API documentation (requires developer access).
7. Semi-Integrated Device Flow, NexGO SmartConnect API
For card-present transactions, PeakPOS communicates with NexGO's pre-certified Integrator app via the SmartConnect API. SmartConnect uses Android Intents (on-device, preferred) or TCP (port 8765, cross-device) to pass JSON messages. Card data never touches our PeakPOS app or servers.
7.1 SmartConnect Flow
┌──────────────┐ ┌───────────────────────┐ ┌─────────────┐
│ PeakPOS App │ │ NexGO Integrator App │ │ TransIT │
│ (Android) │ │ (SmartConnect API, │ │ Multipass │
│ │ │ Pre-Certified EMV) │ │ API │
└──────┬───────┘ └──────────┬────────────┘ └──────┬──────┘
│ │ │
│ 1. Android Intent │ │
│ w/ JSON request │ │
│───────────────────────>│ │
│ │ │
│ │ 2. Integrator takes │
│ │ screen, prompts │
│ │ insert/tap/swipe │
│ │ │
│ │ 3. Encrypts card data, │
│ │ sends to TransIT │
│ │────────────────────────>│
│ │ │
│ │ 4. TransIT processes, │
│ │ returns auth result │
│ │<────────────────────────│
│ │ │
│ 5. Intent result │ │
│ w/ JSON response │ │
│ (transdata extra) │ │
│<───────────────────────│ │
│ │ │
│ 6. PeakPOS logs result │ │
│ via Processing │ │
│ Microservice │ │
7.2 SmartConnect Request Format
PeakPOS sends an Android Intent with a JSON payload:
val intent = Intent("android.intent.action.Integrator")
intent.putExtra("Input", requestJson.toString())
startActivityForResult(intent, REQUEST_CODE)
Sale Request
{
"action": {
"accountType": 0,
"processor": "<TSYS_TI>",
"receipt": true,
"signature": true,
"manual": true
},
"payment": {
"type": "Sale",
"amount": "10.00",
"tip_amount": "0.00",
"cash_back": "0.00"
}
}
Auth (Pre-Authorization) Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Auth",
"amount": "10.00",
"tip_amount": "0.00"
}
}
Capture (Post-Auth) Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Capture",
"amount": "10.00",
"tip_amount": "0.00",
"transaction_id": "<transactionID from Auth>",
"trace_no": "<traceNo from Auth>"
}
}
Either transaction_id or trace_no must be included. If both are present, transaction_id takes precedence.
Void Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Void",
"transaction_id": "<transactionID>",
"trace_no": "<traceNo>"
}
}
Return (Refund) Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Return",
"amount": "5.00"
}
}
Tip Adjustment Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Tip Adjustment",
"amount": "10.00",
"tip_amount": "3.00",
"transaction_id": "<transactionID>",
"trace_no": "<traceNo>"
}
}
Note (EVO-specific): Tip Adjustment on EVO can only modify the tip, not the base amount. Confirm TransIT behavior matches.
Action Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
processor | String | M | Processor identifier. Can be left blank if only one Integrator is installed. |
receipt | boolean | M | Print receipt on terminal's integrated printer |
signature | boolean | O | Prompt for on-screen signature after approval |
manual | boolean | O | Allow manual/keyed card entry (vs. swipe/EMV/tap only) |
accountType | String | O | 0 = Automatic, 1 = Credit, 2 = Debit |
Payment Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
type | String | M | Transaction type: Sale, Auth, Capture, Void, Return, Tip Adjustment, BatchClose, BatchDetail, BatchSummary, Reprint, getLastStatus, TerminalInfo, EBTFSPurchase, EBTCBPurchase, EBTBalance, EBTFSReturn |
amount | String | M | Base transaction amount (e.g., "10.00") |
tip_amount | String | O | Tip amount. If omitted, Integrator prompts user for tip on screen. |
cash_back | String | O | Cash back amount |
transaction_id | String | O/M | Host transaction ID (for Void, Capture, Tip Adjust). Either this or trace_no required. |
trace_no | String | O/M | Local terminal transaction ID (for Void, Capture, Tip Adjust) |
panDataToken | String | O | Payment token for card-on-file transactions (see section 7.5) |
taxRate | String | O | Tax rate percentage (TSYS Sierra only) |
customTaxOneRate | String | O | Custom tax rate (TSYS Sierra only) |
customTaxTwoRate | String | O | Custom tax rate (TSYS Sierra only) |
7.3 SmartConnect Response Format
Response is returned via the Intent result in onActivityResult():
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val response = data?.getStringExtra("transdata") // JSON response
val signature = data?.getByteArrayExtra("signature") // Signature bitmap (if requested)
}
Sale/Auth Response Fields
| Field | Type | Description |
|---|---|---|
batchID | String | Batch number (e.g., "000004") |
transactionID | String | Host transaction ID, use for Void, Capture, Tip Adjust |
traceNo | String | Local terminal transaction ID |
transactionType | String | e.g., "Credit Sale", "Pre-Auth", "Void", "Credit Refund", "Add Tip" |
baseAmount | String | Base amount processed (excluding tip) |
tipAmount | String | Tip amount ("0.00" if none) |
cashbackAmount | String | Cash back amount ("0.00" if none) |
processedAmount | String | Total processed (base + tip + fees) |
resultCode | String | "00" = approved. Non-zero = declined/error. -501 = cancelled. |
cardNumber | String | Last 4 digits only (e.g., "0010") |
cardIssuer | String | Card brand (e.g., "VISA", "MASTERCARD") |
cardDataEntry | String | Entry method: "SWIPED", "CHIP READ (I)", "CONTACTLESS", "KEYED", "TOKEN" |
panDataToken | String | Payment token for subsequent card-on-file charges |
referenceNumber | String | Host reference number for troubleshooting with processor |
authorizationCode | String | Processor authorization code |
merchantId | String | Merchant account ID used for processing |
applicationVersion | String | Integrator app version (e.g., "N08.011.066_49") |
fwVersion | String | Terminal firmware version |
emvTags[] | JSON Array | EMV tag key-value pairs (e.g., "9F06=A0000000031010") |
cardHolder | String | Cardholder name from card (if present) |
accountType | String | "Credit" or "Debit" (TSYS Sierra only) |
cardBIN | String | Card BIN (TSYS Sierra only) |
emvAppLabel | String | EMV application label (e.g., "MasterCard") |
| Full JSON response | JSON Object | Store for debugging |
Sample Sale Response (TSYS TransIT)
{
"packetID": "XCRT",
"packetData": {
"batchID": "000004",
"transactionID": "53ABB10B5BFF49C7A20B456AD7040BBD",
"traceNo": "6",
"transactionType": "Credit Sale",
"baseAmount": "1.00",
"tipAmount": "0.00",
"cashbackAmount": "0.00",
"resultCode": "00",
"cardNumber": "0010",
"cardIssuer": "VISA",
"cardDataEntry": "CHIP READ (I)",
"referenceNumber": "000001560420",
"authorizationCode": "556475",
"merchantId": "000000001168579",
"applicationVersion": "N08.011.066_49",
"fwVersion": "v4.5.5",
"emvTags": [
"9F06=A0000000031010",
"5F2A=0840",
"9F36=0042",
"9F26=6206484E62C59D54",
"9F02=000000000100",
"82=5C00",
"95=42C0008000"
],
"cardHolder": "TEST CARD 01"
}
}
SmartConnect Result Codes
| resultCode | Description |
|---|---|
00 | Success / Approved |
| Non-zero | Declined, check processor response for meaning |
-50X | General application error |
-501 | Transaction cancelled by user |
7.4 SmartConnect Transaction Type Support (TSYS TransIT)
| Transaction Type | TSYS TransIT | Notes |
|---|---|---|
| Sale | ✅ | Credit + Debit |
| Credit/Debit Selection | ❌ | Not supported on TransIT |
| Auth (Pre-Auth) | ✅ | |
| Capture (Post-Auth) | ✅ | Reference by transaction_id or trace_no |
| Void | ✅ | |
| Return (Refund) | ✅ | |
| Tip Adjustment | ✅ | |
| Reprint | ✅ | Reprint last receipt / retrieve last response JSON |
| getLastStatus | ✅ | Get raw JSON from last transaction |
| BatchClose | ✅ | Settle the open batch (see section 9) |
| BatchDetail | ✅ | Per-transaction batch listing |
| BatchSummary | ✅ | Aggregate batch totals |
| EBTFSPurchase | ✅ | EBT Food Stamp |
| EBTCBPurchase | ✅ | EBT Cash Benefit |
| EBTBalance | ❌ | Not supported on TransIT |
| EBTFSReturn | ✅ | |
| EBTFSVoucher | ✅ | |
| EBTFSVoucherReturn | ❌ | Not supported on TransIT |
| Gift (all types) | ❌ | Not supported on TransIT |
| DEVICE_INFO | ❌ | Not supported on TransIT |
7.5 SmartConnect Token Payments (panDataToken)
SmartConnect supports sending a panDataToken in the request to charge a previously tokenized card without requiring the physical card:
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": {
"type": "Sale",
"panDataToken": "03a0106a-d62a-4a47-9301-6060c1774e26...",
"amount": "10.00",
"tip_amount": "0.00"
}
}
On approved transactions, the response includes a panDataToken field that can be stored for subsequent card-on-file use.
⚠️ SmartConnect Replay Boundary: As of SmartConnect API rev. 1.18 (Oct 2024),
panDataTokenreplay is documented for the EVO Integrator application but not for the TSYS TransIT Integrator. Treat SmartConnect token replay as unsupported for TransIT unless NexGO certification evidence says otherwise. Card-present token capture can still store returned tokens, but recurring or card-on-file replay must use the gateway's server-to-server TransIT token path rather than sendingpanDataTokenback through SmartConnect.
7.6 SmartConnect Communication Modes
| Mode | When to Use | Details |
|---|---|---|
| Android Intent (preferred) | PeakPOS runs on same device as Integrator | intent.setAction("android.intent.action.Integrator"), auto-starts Integrator if not running |
| TCP | External device initiating payment | Connect to terminal IP on port 8765. Same JSON format. Must handle socket lifecycle. |
PeakPOS uses Android Intent when it runs on the same NexGO device as the Integrator.
7.7 PeakPOS to SmartConnect Response Mapping
The PeakPOS app must map SmartConnect response fields to the monorepo's card_authorizations schema:
| SmartConnect Field | Monorepo Schema Field | Notes |
|---|---|---|
transactionID | processor_transaction_id | Host transaction ID |
traceNo | terminal_trace_no (new) | Local terminal ID, needed for Void/Capture/Tip Adjust |
batchID | batch_id | |
baseAmount | amount | Parse string to cents |
tipAmount | tip_amount | |
cashbackAmount | cashback_amount | |
processedAmount | processed_amount | |
resultCode | response_code | "00" = approved |
authorizationCode | approval_code | |
cardNumber | card_last_four | Last 4 only |
cardIssuer | card_brand | |
cardDataEntry | card_data_entry | SWIPED/CHIP/CONTACTLESS/KEYED/TOKEN |
panDataToken | token | Store for card-on-file |
referenceNumber | reference_number | |
merchantId | processor_merchant_id | |
emvTags[] | emv_data (JSONB) | Parse array to structured object |
cardHolder | cardholder_name | |
emvAppLabel | emv_app_label | |
| Full JSON response | raw_response (JSONB) | Store complete response for debugging |
8. Tokenization Strategy, Guardian Tokenization
TransIT provides tokenization through Guardian Tokenization, a TSYS-native token vault that replaces card PANs with non-reversible token values. Guardian tokens are the foundation for recurring billing, card-on-file, and refund-on-original-card flows.
8.1 How Guardian Tokenization Works
- Token generation: On any transaction where
tokenRequired: "Y", TransIT returns atokenIDalongside the authorization response - Token usage: Subsequent transactions send
tokenIDin thecardNumberfield withcardDataSource: "TOKEN", no raw card data needed - Token scope: Tokens are scoped to our
deviceID. A token generated under one deviceID cannot be used under another - Token persistence: Tokens remain valid as long as the underlying card is valid, no separate expiration on the token itself
8.2 Token Lifecycle
┌──────────────────────┐
│ Transaction with │
│ tokenRequired: "Y" │
└──────────┬───────────┘
│
v
┌──────────────────────┐
│ ACTIVE │ <- Token returned in response
│ (usable for charges) │
└──────────┬───────────┘
│
┌───────┼────────┐
│ │ │
v v v
┌──────┐ ┌──────┐ ┌─────────┐
│ Used │ │ Card │ │ Explicit│
│ for │ │ exp │ │ delete │
│ TOKEN│ │ or │ │ request │
│ txns │ │ lost │ │ │
└──────┘ └──┬───┘ └────┬────┘
│ │
v v
┌──────────┐ ┌──────────┐
│ INACTIVE │ │ DELETED │
│ (decline │ └──────────┘
│ on use) │
└────┬─────┘
│
v
┌──────────┐
│ DELETED │ (after cleanup period)
└──────────┘
8.3 Token Generation, Card Present vs. Card Not Present
| Channel | How Token is Generated | Card Data Source |
|---|---|---|
| Card Present (NexGO) | SmartConnect semi-integration: NexGO Integrator handles card capture, sends to TransIT, panDataToken returned in SmartConnect response, PeakPOS stores token | EMV, CONTACTLESS, SWIPE |
| Card Not Present (Server-to-Server) | Merchant sends card data via API, Processing Service sends to TransIT with tokenRequired: "Y", token stored | MANUAL, INTERNET |
| Card Not Present (Hosted Payment) | Customer enters card in hosted page/iFrame, TransIT handles PCI-scoped tokenization, token returned to our backend | INTERNET |
| Existing Token | Subsequent charges (recurring, card-on-file) use stored tokenID | TOKEN |
⚠️ Card-Present Token Replay Boundary: SmartConnect returns
panDataTokenin responses, but replaying that token in a SmartConnect request is only documented for EVO. For TSYS TransIT, route card-present token replay through the gateway's server-to-server TransIT token path unless NexGO certification evidence explicitly approves SmartConnect replay.
8.4 Token-to-Customer Mapping
Our gateway maintains the relationship between Guardian tokens and customers in the stored_tokens table (Online Txn Service):
Customer "cust_001" (Jane Doe)
├── tok_visa_1234 → Visa ****1234, exp 12/27 (ACTIVE, primary)
├── tok_mc_5678 → MC ****5678, exp 03/28 (ACTIVE)
└── tok_visa_9012 → Visa ****9012, exp 01/26 (INACTIVE, expired)
Customer "cust_002" (John Smith)
└── tok_amex_3456 → Amex ****3456, exp 09/27 (ACTIVE, primary)
Rules:
- One customer can have multiple stored tokens (multiple cards)
- Each token links to exactly one
customer_idand onemerchant_id - Tokens are merchant-scoped: a token stored by merchant A cannot be used by merchant B (Guardian's deviceID scoping enforces this at the TSYS level)
- Customers can designate a "primary" token for default recurring charges
- On card update (new expiry, replacement card), merchant must create new token. Guardian tokens are immutable
8.5 Token Usage for Recurring Billing
The subscription billing engine (Processing Microservice) uses Guardian tokens to execute recurring charges:
1. Billing job queries due subscriptions
2. For each subscription, retrieves stored tokenId
3. Sends TOKEN sale to TransIT:
{
"SaleTransaction": {
"deviceID": "...",
"transactionKey": "...",
"cardNumber": "<tokenID>", <- Guardian token, NOT a PAN
"cardDataSource": "TOKEN",
"transactionAmount": "49.99",
"transactionIndustryType": "EC",
"tokenRequired": "N" <- Already have token, don't need new one
}
}
4. TransIT resolves token, charges underlying card
5. Response handled same as any sale (approved/declined)
Decline handling for recurring: If a recurring TOKEN charge is declined, the token status is not automatically changed. The subscription enters retry/dunning flow. Only persistent declines (card cancelled, account closed) should trigger token deactivation.
8.6 NMI Token Migration Strategy
The existing PeakPOS monorepo uses NMI as its payment processor. Customers already have NMI tokens stored in the card_authorizations table (field: token). When migrating merchants from NMI to Peak Gateway (TSYS TransIT):
NMI tokens are not compatible with Guardian tokens. Migration requires re-tokenization.
| Migration Approach | Description | Pros | Cons |
|---|---|---|---|
| Gradual re-tokenization | On next customer transaction (any channel), capture card data, generate Guardian token, store alongside the NMI token, subsequent charges use Guardian | No bulk migration, zero downtime | Stale customers may never re-tokenize |
| Card-on-file migration | For active recurring customers, use NMI's card-on-file data to generate a Guardian token via a one-time batch process | Complete migration for active subscribers | Requires NMI to allow one final charge/verification per token; PCI implications of handling card data in transit |
| Customer re-entry | Notify customers to re-enter card data via hosted payment page | Cleanest from PCI perspective | Worst customer experience; guaranteed churn |
Recommended: Gradual re-tokenization as default, with card-on-file migration for merchants with active recurring/subscription billing that must continue uninterrupted. Track migration status per-merchant:
merchant.tokenMigrationStatus:
NOT_STARTED -> IN_PROGRESS -> COMPLETED
Per stored token:
nmi_token: "nmi_abc123" (original)
guardian_token: "tok_xyz789" (new, null until re-tokenized)
migration_status: PENDING | MIGRATED
8.7 Token Security & PCI Considerations
- Guardian tokens are non-reversible, cannot derive card PAN from token
- Tokens are scoped to our deviceID, compromised token is useless without our TransIT credentials
- Our systems never store raw card data (PANs, CVVs), only Guardian token IDs
- Card-present tokens: card data never touches our servers (NexGO semi-integration handles encryption)
- Card-not-present tokens: raw card data is transmitted to TransIT in the initial tokenization call, but our gateway acts as a pass-through, we log the token, not the PAN
- Hosted payment tokens: card data enters TransIT's hosted page directly, never touches our infrastructure
NOTE: Verify Guardian token-management changes against the authenticated TransIT documentation and certification fixtures before changing request serialization. Token deletion and deactivation APIs may have additional processor-specific parameters.
9. Settlement & Batch Management (via SmartConnect)
TransIT is a host-capture platform, batch management happens on the TSYS cloud. We manage batches ourselves through SmartConnect's batch operations on the terminal, not through the TSYS Merchant Center portal.
Settlement Modes
| Mode | Description |
|---|---|
| Scheduled | Auto-settle at a configured time daily. Default dev batch closing: 10:30 PM |
| On-Demand | Manual batch close via SmartConnect BatchClose request from PeakPOS or gateway |
SmartConnect Batch Operations
BatchDetail, View All Transactions in Current Batch
// Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": { "type": "BatchDetail" }
}
// Response
{
"packetID": "XCRT",
"packetData": {
"batchID": "000004",
"transactionType": "BatchDetail",
"timestamp": "2026-03-08 13:51",
"transactions": [
{
"traceNo": "1",
"transactionID": "8420679",
"transactionType": "Sale",
"referenceNumber": "123456789",
"authorizationCode": "556475",
"cardDataEntry": "SWIPE",
"cardHolder": "JOHN DOE",
"baseAmount": "10.00",
"tipAmount": "2.00",
"cashbackAmount": "0.00",
"feeAmount": "0.50",
"taxAmount": "0.10",
"processedAmount": "12.60",
"timestamp": "2026-03-08 11:30"
}
]
}
}
Use case: Display batch contents in PeakPOS, identify problematic transactions before settlement, reconcile terminal batch against gateway records.
BatchSummary, Aggregate Batch Totals
// Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": { "type": "BatchSummary" }
}
// Response
{
"packetID": "XCRT",
"packetData": {
"batchID": "000004",
"transactionType": "BatchSummary",
"timestamp": "2026-03-08 13:51",
"summary": [{
"cashSaleCount": "1",
"cashSaleAmount": "5.00",
"cashReturnCount": "0",
"cashReturnAmount": "0.00",
"totalCash": "5.00",
"creditDebitCount": "4",
"creditDebitAmount": "48.00",
"creditDebitReturnCount": "1",
"creditDebitReturnAmount": "5.00",
"totalCreditDebit": "43.00",
"ebtCount": "1",
"ebtAmount": "12.00",
"ebtReturnCount": "0",
"ebtReturnAmount": "0.00",
"totalEbt": "12.00",
"giftRedeemCount": "0",
"giftRedeemAmount": "0.00",
"giftActivateCount": "0",
"giftActivateAmount": "0.00",
"giftReloadCount": "0",
"giftReloadAmount": "0.00",
"totalAmount": "60.00"
}]
}
}
Use case: End-of-day summary display, quick reconciliation check before settling.
BatchClose, Settle the Batch
// Request
{
"action": { "processor": "<TSYS_TI>", "receipt": true },
"payment": { "type": "BatchClose" }
}
// Response (TSYS TransIT-specific)
{
"packetID": "XCRT",
"packetData": {
"batchID": "000004",
"transactionType": "BatchClose",
"resultCode": "00",
"merchantId": "000000001168579",
"applicationVersion": "N08.011.066_49",
"fwVersion": "v4.5.5",
"settlementTime": "2026/03/08 22:30:01",
"creditSaleCount": "6",
"creditSaleAmount": "120.00",
"creditReturnCount": "1",
"creditReturnAmount": "5.00",
"debitSaleCount": "2",
"debitSaleAmount": "30.00",
"debitReturnCount": "0",
"debitReturnAmount": "0.00",
"transITHostResponseObject": {
"BatchCloseResponse": {
"batchInfo": {
"returnAmount": "500",
"returnCount": "1",
"sICCODE": "5999",
"saleAmount": "15000",
"saleCount": "8"
},
"responseCode": "A0000",
"responseMessage": "Success",
"status": "PASS"
}
},
"hostReturnAmount": "5.00",
"hostReturnCount": "1",
"hostSaleAmount": "150.00",
"hostSaleCount": "8"
}
}
TransIT-specific fields in BatchClose response:
| Field | Description |
|---|---|
transITHostResponseObject | Full parsed host response from TransIT (JSON object) |
hostSaleCount / hostSaleAmount | Host-reported sale totals, use for reconciliation against terminal counts |
hostReturnCount / hostReturnAmount | Host-reported return totals |
Reconciliation: Compare terminal-reported counts (creditSaleCount, etc.) against host-reported counts (hostSaleCount, etc.). Discrepancies indicate transactions that were processed locally but not received by the host, or vice versa.
Batch Management Workflow
1. During business day: PeakPOS processes Sales, Auths, Voids, Returns
2. Before settlement: PeakPOS calls BatchDetail to review transactions
- Identify bad/problematic transactions
- Void incorrect transactions (before batch closes)
- Apply tip adjustments
3. At configured time (10:30 PM default): PeakPOS sends BatchClose
- Or: Gateway triggers BatchClose via TCP to terminal
4. After settlement: Gateway compares terminal response vs. host response
- Flag discrepancies for manual review
- Store reconciliation results in CDE Spanner
5. New batch opens automatically after settlement
Void Rules
| Void Type | Availability |
|---|---|
| Full void | Before batch settlement only |
| Partial void | Before batch settlement only |
| After settlement | Must use refund (Return) instead |
10. Recurring & Scheduled Payments via TransIT
How Recurring Works at the TransIT Level
TransIT does not have a native subscription/recurring billing engine. Recurring billing is implemented by our gateway, the Processing Microservice's subscription engine schedules and executes TOKEN-based sale transactions at configured intervals.
TransIT's Role in Recurring
| Responsibility | Owner |
|---|---|
| Subscription creation, lifecycle, scheduling | Peak Gateway (Processing Microservice) |
| Dunning logic (retries on decline) | Peak Gateway (Processing Microservice) |
| Executing the actual charge | TransIT (via TOKEN sale) |
| Token storage & resolution | TransIT (Guardian Tokenization) |
| Token-to-customer mapping | Peak Gateway (Online Txn Service) |
Transaction Flow for a Recurring Charge
Peak Gateway (scheduler)
│
│ 1. Query subscriptions due today
│ 2. For each: look up Guardian tokenId
│
v
Processing Microservice
│
│ 3. POST SaleTransaction with cardDataSource: "TOKEN"
│
v
TransIT Multipass API
│
│ 4. Resolve Guardian token, card PAN
│ 5. Process sale against issuer
│ 6. Return approval/decline
│
v
Processing Microservice
│
│ 7a. Approved, update subscription cycle, publish event
│ 7b. Declined, increment failed_attempts, schedule retry
Industry Type for Recurring
- Recurring charges for online merchants:
transactionIndustryType: "EC"(E-Commerce) - Recurring charges for mail/phone order merchants:
transactionIndustryType: "DM"(Direct Marketing) - The industry type is set per-merchant in their configuration, not per-subscription
Certification Note
Recurring billing uses the same TOKEN-based sale flow as card-on-file payments. No separate certification is needed for recurring billing specifically, it falls under the Card Not Present (server-to-server) certification scope. Once CNP is certified, recurring billing works automatically.
11. Certification Scope
Pre-Certified (No Additional Certification Needed)
| Capability | Status |
|---|---|
| Level 3 EMV (Card Present) | Pre-certified, works out of the box with NexGO semi-integration |
Requires Separate Certification
| Capability | Certification | Notes |
|---|---|---|
| Back Office | Separate cert | NexGO integration, Sales/Refund/Void/Batch Management, Tokenization (if needed) |
| Card Not Present | Separate cert | Server-to-server, not pre-certified |
| Hosted Payment 2.0 | Separate cert | Sale-only, "really simple certification process" |
| Apple Pay | Separate cert | After base CNP certification |
| Google Pay | Separate cert | After base CNP certification |
Key Certification Rule
Adding new capabilities does NOT extend existing certification, only new items need testing.
Each new feature or payment method requires its own certification testing with TSYS. Plan certification batches strategically to minimize re-testing.
12. Error Handling & Retry Strategy
| Scenario | Action |
|---|---|
| Network timeout to TransIT | Retry with exponential backoff (max 3 attempts, 1s -> 2s -> 4s) |
| TransIT returns 5xx | Retry with exponential backoff (max 3 attempts) |
| TransIT returns 4xx | Do NOT retry. Log error, return failure to caller. |
| Duplicate transaction suspected | Use idempotency key (Spanner) to prevent double-charge |
| TransIT unreachable | Queue transaction in Pub/Sub for retry. Alert ops. |
Idempotency
Every transaction request includes a client-generated externalReferenceID (UUID). If a timeout occurs and we retry, TransIT will recognize the duplicate and return the original response.
13. References
- TransIT Developer Portal: https://developerportal.transit-pass.com/
- TransIT API Revision History: https://developerportal.transit-pass.com/developerportal/resources/dist/#/api-specs/
- TSYS Semi-Integrated EMV: https://tsysacquiring.my.site.com/partner/s/TSYS-Semi-integrated-EMV-Solution
- TSYS Developers: https://developers.tsys.com/api
- NexGO SmartConnect API:
NEXGO SmartConnect 21-0625.pdf(Rev. 1.18, Oct 2024) , local:Nexgo-Android-Public/Semi-Integration/ - NexGO SmartSDK: v3.08.012 AAR , local:
Nexgo-Android-Public/Nexgo-Smart-SDK/Library and Release Documentation/ - NexGO SmartSDK API Reference:
Nexgo_SmartPos_API_3.08_012.pdf, local:Nexgo-Android-Public/Nexgo-Smart-SDK/Library and Release Documentation/ - NexGO Developer Contact: sdk@nexgo.us