Skip to main content

TransIT Integration

1. Overview

This document specifies the monorepo's TSYS TransIT integration via the shared Kotlin library libs/transit-client.

  • libs/transit-client is the canonical API wrapper for TransIT request/response models, wrappers, retries, and exception handling.
  • services/processing integrates through TransitPaymentProcessor, which maps service DTOs to transit-client DTOs and delegates to TransitClient.
  • Other services can also use the same library for non-processing workflows (for example merchant activation and provisioning).

2. TransIT API Details

Environments

EnvironmentBase URLPurpose
Sandboxhttps://stagegw.transnox.com/servlets/TransNox_API_ServerDevelopment and testing
Productionhttps://gateway.transit-pass.com/servlets/TransNox_API_ServerLive transactions

NOTE: These URLs must be confirmed via the TransIT Developer Portal once we have authenticated access. The sandbox URL is derived from publicly available integration guides and may have changed.

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)

BLOCKER: We need these credentials from TSYS. Cannot proceed with integration testing without them.

API Version

  • Current: API v10.0 (October 2024 release)
  • Our target: Latest stable version at time of integration
  • Format: JSON (XML also supported but we will use JSON exclusively)

Integration Architecture (Current Code)

services/* -> libs/transit-client: TransitClient -> TransIT endpoint
  • TransitClient centralizes 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 through TransitPaymentProcessor
    • services/management: merchant/activation related integration points
    • services/merchant-onboarding: activation and provisioning retry orchestration
  • Exception boundary is split by layer:
    • TransitClient throws TransitException.*
    • 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)

OperationRequest WrapperKey FieldsKotlin Method
SaleSaletransactionAmount, cardNumber/tokenID, cardDataSource, transactionIndustryType, externalReferenceIDsale(credentials, SaleRequest)
AuthAuthtransactionAmount, cardNumber/tokenID, cardDataSource, transactionIndustryType, externalReferenceIDauthorize(credentials, AuthRequest)
CaptureCapturetransactionID, transactionAmount, externalReferenceIDcapture(credentials, CaptureRequest)
VoidVoidtransactionID, externalReferenceIDvoid(credentials, VoidRequest)
Return / RefundReturntransactionID or card fields, transactionAmount, externalReferenceIDrefund(credentials, ReturnRequest)
TipAdjustTipAdjustmenttip, transactionID, externalReferenceIDtipAdjust(credentials, TipAdjustRequest)
L2L3AdjustTransactionAdjustmenttransactionID, level2Data, level3Data, externalReferenceIDl2l3Adjust(credentials, L2L3AdjustRequest)
CardVerificationCardVerificationcardNumber, expirationDate, CVV2, externalReferenceIDcardVerification(credentials, CardVerificationRequest)
BalanceInquiryBalanceInquirycard/account lookup fields + externalReferenceIDbalanceInquiry(credentials, BalanceInquiryRequest)
BatchCloseBatchCloseoptional batch context + externalReferenceIDbatchClose(credentials, BatchCloseRequest)
MerchantActivationActivateMerchantmerchant onboarding fieldsmerchantActivation(credentials, MerchantActivationRequest)
ForcedAuthForcedAuthauth fields + force-auth detailsforcedAuth(credentials, ForcedAuthRequest)
CardAuthenticationCardAuthentication3DS/card-auth fieldscardAuthentication(credentials, CardAuthenticationRequest)
DebitSaleDebitSaledebit card/PIN transaction fieldsdebitSale(credentials, DebitSaleRequest)
DebitReturnDebitReturndebit refund fieldsdebitReturn(credentials, DebitReturnRequest)
CashSaleCashSalecash sale amount/order fieldscashSale(credentials, CashSaleRequest)
NotificationNotificationcallback/event payload fieldsnotification(credentials, NotificationRequest)
GenerateReceiptGenerateReceipttransactionID, receipt delivery fieldsgenerateReceipt(credentials, GenerateReceiptRequest)
SearchTransactionSearchTransactionsearch criteria (transactionID, refs, date ranges)searchTransaction(credentials, SearchTransactionRequest)
GenerateKeyGenerateKeykey generation payload (no merchant credentials wrapper)generateKey(GenerateKeyRequest)

3.2 Newer Operations

Network Tokenization (4)

OperationRequest WrapperKey FieldsKotlin Method
GenerateTokenGenerateTokenPAN/tokenization input + customer contextgenerateToken(credentials, GenerateTokenRequest)
GenerateTokenCryptogramGenerateTokenCryptogramtoken + cryptogram context (amount, auth context)generateTokenCryptogram(credentials, GenerateTokenCryptogramRequest)
GetTokenStatusGetTokenStatustoken identifier/status lookup fieldsgetTokenStatus(credentials, GetTokenStatusRequest)
GetTokenCardMetaDataGetTokenCardMetaDatatoken identifier for metadata retrievalgetTokenCardMetadata(credentials, GetTokenCardMetaDataRequest)

EBT (5)

OperationRequest WrapperKey FieldsKotlin Method
EbtCashBenefitPurchaseEbtCashBenefitPurchaseEBT account + purchase amount fieldsebtCashBenefitPurchase(credentials, EbtCashBenefitPurchaseRequest)
EbtCashWithdrawalEbtCashWithdrawalEBT account + withdrawal amount fieldsebtCashWithdrawal(credentials, EbtCashWithdrawalRequest)
EbtElectronicVoucherEbtElectronicVouchervoucher ID + voucher redemption fieldsebtElectronicVoucher(credentials, EbtElectronicVoucherRequest)
EbtFoodStampPurchaseEbtFoodStampPurchasefood-stamp purchase amount/account fieldsebtFoodStampPurchase(credentials, EbtFoodStampPurchaseRequest)
EbtReturnEbtReturnoriginal EBT transaction reference + return amountebtReturn(credentials, EbtReturnRequest)

eInvoice (7)

OperationRequest WrapperKey FieldsKotlin Method
CreateInvoiceCreateInvoiceinvoice amount, recipient, due and metadata fieldscreateInvoice(credentials, CreateInvoiceRequest)
CancelInvoiceCancelInvoiceinvoice identifier + cancel reason/contextcancelInvoice(credentials, CancelInvoiceRequest)
GenerateInvoiceEmailGenerateInvoiceEmailinvoice identifier + recipient email contextgenerateInvoiceEmail(credentials, GenerateInvoiceEmailRequest)
InvoiceAchInvoiceAchinvoice identifier + ACH payment detailsinvoiceAch(credentials, InvoiceAchRequest)
InvoiceSaleInvoiceSaleinvoice identifier + card payment detailsinvoiceSale(credentials, InvoiceSaleRequest)
SearchInvoiceSearchInvoiceinvoice search criteria (ID, merchant/customer/date status)searchInvoice(credentials, SearchInvoiceRequest)
UpdateInvoiceUpdateInvoiceinvoice identifier + mutable invoice fieldsupdateInvoice(credentials, UpdateInvoiceRequest)

Digital Wallet (4)

OperationRequest WrapperKey FieldsKotlin Method
DigitalWalletSaleDigitalWalletSalewallet payload + sale amount/contextdigitalWalletSale(credentials, DigitalWalletSaleRequest)
DigitalWalletAuthDigitalWalletAuthwallet payload + auth amount/contextdigitalWalletAuth(credentials, DigitalWalletAuthRequest)
SecureAuthSecureAuthsecure wallet auth payload (network token + cryptogram)secureAuth(credentials, SecureAuthRequest)
SecureSaleSecureSalesecure wallet sale payload (network token + cryptogram)secureSale(credentials, SecureSaleRequest)

ACH (4)

OperationRequest WrapperKey FieldsKotlin Method
AchSaleAchrouting/account details, amount, SEC contextachSale(credentials, AchSaleRequest)
AchReturnAchReturnoriginal ACH transaction reference + return reasonachReturn(credentials, AchReturnRequest)
AchResubmitAchResubmitoriginal ACH reference + resubmission detailsachResubmit(credentials, AchResubmitRequest)
AchVerificationAchVerificationaccount verification (routing/account/customer fields)achVerification(credentials, AchVerificationRequest)

Signature and EMV Enrichment

OperationRequest WrapperKey FieldsKotlin Method
UpdateSignatureUpdateTransactionSignaturetransactionID, signature payload (signatureData/image fields)updateSignature(credentials, UpdateSignatureRequest)
EMV EnrichmentTransactionAdjustment plus EMV fields on sale/auth requestsL2/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 TransitClient as 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, and batchClose
  • maps transit responses back to TransactionResult
  • maps TransitException types to service-layer behavior (declines -> declined result, auth/network/server errors -> service exceptions)

4. Transaction Industry Types

CodeTypeUse Case
RERetailPeakPOS in-store transactions
ECE-CommerceOnline payment website
RSRestaurantFuture vertical
HCHealthcareFuture vertical
DMDirect MarketingMail/phone orders, actively used now
LDLodgingOut of scope (Sierra)

5. Card Data Sources

CodeDescriptionOur Usage
MANUALManually keyed card numberManagement portal manual entry, online payments
SWIPEMagnetic stripePre-certified terminal
EMVChip insertionPre-certified terminal
CONTACTLESSNFC tapPre-certified terminal
TOKENPreviously tokenized cardRecurring payments, stored cards
INTERNETE-commerce transactionOnline payment website

6. Response Codes

Approval Codes (Prefix: A)

CodeMeaning
A0000Approved
A0001Approved (partial)

Decline Codes (Prefix: D)

CodeMeaning
D0092Declined, general
D1218Declined, invalid card
D1219Declined, expired card
D1220Declined, insufficient funds
D1221Declined, CVV mismatch
D1222Declined, AVS mismatch
D1223Declined, card restricted
D1224Declined, do not honor
D1225Declined, pickup card
D1226Declined, invalid transaction

Error Codes (Prefix: E)

CodeMeaning
E0913Error, invalid device credentials
E0XXXError, 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

FieldTypeRequiredDescription
processorStringMProcessor identifier. Can be left blank if only one Integrator is installed.
receiptbooleanMPrint receipt on terminal's integrated printer
signaturebooleanOPrompt for on-screen signature after approval
manualbooleanOAllow manual/keyed card entry (vs. swipe/EMV/tap only)
accountTypeStringO0 = Automatic, 1 = Credit, 2 = Debit

Payment Field Reference

FieldTypeRequiredDescription
typeStringMTransaction type: Sale, Auth, Capture, Void, Return, Tip Adjustment, BatchClose, BatchDetail, BatchSummary, Reprint, getLastStatus, TerminalInfo, EBTFSPurchase, EBTCBPurchase, EBTBalance, EBTFSReturn
amountStringMBase transaction amount (e.g., "10.00")
tip_amountStringOTip amount. If omitted, Integrator prompts user for tip on screen.
cash_backStringOCash back amount
transaction_idStringO/MHost transaction ID (for Void, Capture, Tip Adjust). Either this or trace_no required.
trace_noStringO/MLocal terminal transaction ID (for Void, Capture, Tip Adjust)
panDataTokenStringOPayment token for card-on-file transactions (see section 7.5)
taxRateStringOTax rate percentage (TSYS Sierra only)
customTaxOneRateStringOCustom tax rate (TSYS Sierra only)
customTaxTwoRateStringOCustom 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

FieldTypeDescription
batchIDStringBatch number (e.g., "000004")
transactionIDStringHost transaction ID, use for Void, Capture, Tip Adjust
traceNoStringLocal terminal transaction ID
transactionTypeStringe.g., "Credit Sale", "Pre-Auth", "Void", "Credit Refund", "Add Tip"
baseAmountStringBase amount processed (excluding tip)
tipAmountStringTip amount ("0.00" if none)
cashbackAmountStringCash back amount ("0.00" if none)
processedAmountStringTotal processed (base + tip + fees)
resultCodeString"00" = approved. Non-zero = declined/error. -501 = cancelled.
cardNumberStringLast 4 digits only (e.g., "0010")
cardIssuerStringCard brand (e.g., "VISA", "MASTERCARD")
cardDataEntryStringEntry method: "SWIPED", "CHIP READ (I)", "CONTACTLESS", "KEYED", "TOKEN"
panDataTokenStringPayment token for future card-on-file charges
referenceNumberStringHost reference number for troubleshooting with processor
authorizationCodeStringProcessor authorization code
merchantIdStringMerchant account ID used for processing
applicationVersionStringIntegrator app version (e.g., "N08.011.066_49")
fwVersionStringTerminal firmware version
emvTags[]JSON ArrayEMV tag key-value pairs (e.g., "9F06=A0000000031010")
cardHolderStringCardholder name from card (if present)
accountTypeString"Credit" or "Debit" (TSYS Sierra only)
cardBINStringCard BIN (TSYS Sierra only)
emvAppLabelStringEMV application label (e.g., "MasterCard")
Full JSON responseJSON ObjectStore 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

resultCodeDescription
00Success / Approved
Non-zeroDeclined, check processor response for meaning
-50XGeneral application error
-501Transaction cancelled by user

7.4 SmartConnect Transaction Type Support (TSYS TransIT)

Transaction TypeTSYS TransITNotes
SaleCredit + Debit
Credit/Debit SelectionNot supported on TransIT
Auth (Pre-Auth)
Capture (Post-Auth)Reference by transaction_id or trace_no
Void
Return (Refund)
Tip Adjustment
ReprintReprint last receipt / retrieve last response JSON
getLastStatusGet raw JSON from last transaction
BatchCloseSettle the open batch (see section 9)
BatchDetailPer-transaction batch listing
BatchSummaryAggregate batch totals
EBTFSPurchaseEBT Food Stamp
EBTCBPurchaseEBT Cash Benefit
EBTBalanceNot supported on TransIT
EBTFSReturn
EBTFSVoucher
EBTFSVoucherReturnNot supported on TransIT
Gift (all types)Not supported on TransIT
DEVICE_INFONot 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 future use.

⚠️ IMPORTANT: TransIT Token Support Unconfirmed: As of SmartConnect API rev. 1.18 (Oct 2024), panDataToken is documented as supported only on the EVO Integrator application. Support on the TSYS TransIT Integrator is not explicitly confirmed in the documentation. This must be verified with NexGO before relying on SmartConnect-based token replay for card-present recurring. If unsupported, card-present tokenization still works (tokens are returned in responses), but replaying those tokens must go through the gateway's server-to-server TransIT API path, not through SmartConnect.

7.6 SmartConnect Communication Modes

ModeWhen to UseDetails
Android Intent (preferred)PeakPOS runs on same device as Integratorintent.setAction("android.intent.action.Integrator"), auto-starts Integrator if not running
TCPExternal device initiating paymentConnect to terminal IP on port 8765. Same JSON format. Must handle socket lifecycle.

PeakPOS will use Android Intent since our app 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 FieldMonorepo Schema FieldNotes
transactionIDprocessor_transaction_idHost transaction ID
traceNoterminal_trace_no (new)Local terminal ID, needed for Void/Capture/Tip Adjust
batchIDbatch_id
baseAmountamountParse string to cents
tipAmounttip_amount
cashbackAmountcashback_amount
processedAmountprocessed_amount
resultCoderesponse_code"00" = approved
authorizationCodeapproval_code
cardNumbercard_last_fourLast 4 only
cardIssuercard_brand
cardDataEntrycard_data_entrySWIPED/CHIP/CONTACTLESS/KEYED/TOKEN
panDataTokentokenStore for card-on-file
referenceNumberreference_number
merchantIdprocessor_merchant_id
emvTags[]emv_data (JSONB)Parse array to structured object
cardHoldercardholder_name
emvAppLabelemv_app_label
Full JSON responseraw_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

  1. Token generation: On any transaction where tokenRequired: "Y", TransIT returns a tokenID alongside the authorization response
  2. Token usage: Subsequent transactions send tokenID in the cardNumber field with cardDataSource: "TOKEN", no raw card data needed
  3. Token scope: Tokens are scoped to our deviceID. A token generated under one deviceID cannot be used under another
  4. 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

ChannelHow Token is GeneratedCard Data Source
Card Present (NexGO)SmartConnect semi-integration: NexGO Integrator handles card capture, sends to TransIT, panDataToken returned in SmartConnect response, PeakPOS stores tokenEMV, CONTACTLESS, SWIPE
Card Not Present (Server-to-Server)Merchant sends card data via API, Processing Service sends to TransIT with tokenRequired: "Y", token storedMANUAL, INTERNET
Card Not Present (Hosted Payment)Customer enters card in hosted page/iFrame, TransIT handles PCI-scoped tokenization, token returned to our backendINTERNET
Existing TokenSubsequent charges (recurring, card-on-file) use stored tokenIDTOKEN

⚠️ Card-Present Token Replay: SmartConnect returns panDataToken in responses for all processors, but replaying a panDataToken in a SmartConnect request (sending a token instead of requiring a card swipe) is only documented as supported on EVO. TransIT support for panDataToken replay via SmartConnect is unconfirmed, verify with NexGO. If unsupported, card-present token replay must go through the gateway's server-to-server TransIT API using Guardian tokenization, not through SmartConnect.

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_id and one merchant_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 ApproachDescriptionProsCons
Gradual re-tokenizationOn next customer transaction (any channel), capture new card data, generate Guardian token, store alongside NMI token, future charges use GuardianNo bulk migration, zero downtimeStale customers may never re-tokenize
Card-on-file migrationFor active recurring customers, use NMI's card-on-file data to generate a Guardian token via a one-time batch processComplete migration for active subscribersRequires NMI to allow one final charge/verification per token; PCI implications of handling card data in transit
Customer re-entryNotify customers to re-enter card data via hosted payment pageCleanest from PCI perspectiveWorst 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: Exact Guardian Tokenization API fields and token management endpoints must be verified against TransIT documentation once developer access is obtained. Token deletion/deactivation API may have additional 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

ModeDescription
ScheduledAuto-settle at a configured time daily. Default dev batch closing: 10:30 PM
On-DemandManual 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:

FieldDescription
transITHostResponseObjectFull parsed host response from TransIT (JSON object)
hostSaleCount / hostSaleAmountHost-reported sale totals, use for reconciliation against terminal counts
hostReturnCount / hostReturnAmountHost-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 TypeAvailability
Full voidBefore batch settlement only
Partial voidBefore batch settlement only
After settlementMust 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

ResponsibilityOwner
Subscription creation, lifecycle, schedulingPeak Gateway (Processing Microservice)
Dunning logic (retries on decline)Peak Gateway (Processing Microservice)
Executing the actual chargeTransIT (via TOKEN sale)
Token storage & resolutionTransIT (Guardian Tokenization)
Token-to-customer mappingPeak 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)

CapabilityStatus
Level 3 EMV (Card Present)Pre-certified, works out of the box with NexGO semi-integration

Requires Separate Certification

CapabilityCertificationNotes
Back OfficeSeparate certNexGO integration, Sales/Refund/Void/Batch Management, Tokenization (if needed)
Card Not PresentSeparate certServer-to-server, not pre-certified
Hosted Payment 2.0Separate certSale-only, "really simple certification process"
Apple PaySeparate certAfter base CNP certification
Google PaySeparate certAfter 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

ScenarioAction
Network timeout to TransITRetry with exponential backoff (max 3 attempts, 1s -> 2s -> 4s)
TransIT returns 5xxRetry with exponential backoff (max 3 attempts)
TransIT returns 4xxDo NOT retry. Log error, return failure to caller.
Duplicate transaction suspectedUse idempotency key (Spanner) to prevent double-charge
TransIT unreachableQueue 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. Pre-Requisites (Blockers)

ItemStatusImpact
TransIT developer portal accessBLOCKEDCannot verify API specs
Sandbox deviceID + transactionKeyBLOCKEDCannot test any transactions
Pre-certified terminal selectionRESOLVED, NexGONexGO selected for card-present semi-integration
NexGO SDK accessRESOLVEDSmartConnect API docs + SmartSDK v3.08.012 AAR obtained. Semi-integration via Android Intents. See section 7.
Second TSYS meetingNOT SCHEDULEDNeed to clarify NexGO-specific scope and back office cert

14. References