View as Markdown

llms.txt

This guide isn't available right now

This instance couldn't load its guide catalog. The guide returns as soon as the catalog is readable again.

Back to the guides

No such guide

This instance publishes no guide under that address. It may have been renamed, or it may belong to a feature this installation hasn't enabled.

Back to the guides

That guide is part of the product documentation

This guide is written for someone operating WinkPG through its screens rather than integrating against it, so it lives in the application's own help section instead of here. Sign in to WinkPG and open Help to read it.

Back to the guides

Guides Payments

Card-present payments over the API

What a terminal application sends on the transactions API for a magnetic stripe, chip, or contactless read, which reader encryption needs setup first, what each processor does with healthcare amounts, how to tokenize a read without charging it, how to identify the terminal, and how to attribute a webhook to it.

This guide is for developers writing a terminal application that reads a card at a counter, a lane, or a handheld device and sends the read to WinkPG. It covers the fields that make a transaction card-present, what reader encryption needs set up before the first sale, how to tell which terminal took a transaction, and how a webhook receiver attributes an event to that terminal.

Read Quickstart: Direct API first if you haven't taken a payment through the transactions API yet. Everything here is an addition to that request.

When this applies

This guide applies when the terminal application calls the transactions API directly and WinkPG authorizes the payment with the processor. The terminal reads the card, builds the create request, and reads the result from the response.

There's nothing terminal-specific to set up on the API side:

  • No terminal endpoint. A card-present sale is a POST to /api/transactions, the same create request every other payment uses.
  • No device registration. WinkPG doesn't keep a list of devices, and a new terminal doesn't need to be enrolled before it sends its first sale.
  • No per-device credential. The merchant's API key, with the transactions:write scope, is the whole handshake. Every terminal the merchant runs can send with the same key, or you can issue one key per site or per device if you want to revoke one without affecting the others.

A terminal that authorizes with the processor itself and then reports the finished transaction to WinkPG is a different model, and this guide doesn't describe it.

What the merchant configures first

Two things on the merchant's processor profile decide whether a card read reaches the processor as a card-present transaction. Both are set once, by whoever boards the merchant, and apply to every terminal. Neither is configured per device.

  • An active processor profile with a card-present industry, such as Retail or Food/Restaurant. Routing doesn't look at the entry mode, so a card-present request goes to whichever profile the merchant's routing selects. The processor message is built for that profile's industry: on a profile set to Electronic Commerce or Direct Marketing, the message isn't a card-present one, and the chip data may not be sent at all.
  • The processor's terminal number on that profile. Depending on the processor, the field is labeled Terminal Number or Terminal ID. It's the value the processor assigned to the merchant's terminal configuration, and every terminal application sending through the profile shares it.

A merchant with no active processor profile can't take any payment. The create request comes back with the ProcessorNotConfigured result code.

The request

A card-present request is the ordinary create request with three additions: the entry mode, the data the reader produced, and a deviceData block describing the device.

Entry mode

cardData.entryMode says how the card was read on this transaction. It's what makes the transaction card-present, so always send it from a terminal.

entryMode Send it when
Manual The card number was keyed on the terminal. A keyed number is processed as a card-not-present entry unless you also set cardholderPresence (see below).
UnencryptedCardReaderSwipe The magnetic stripe was read and the reader produced cleartext track data.
EncryptedCardReaderSwipe The magnetic stripe was read and the reader encrypted the track data.
Icc The chip was read by contact (a dip).
Proximity The card or phone was tapped (contactless).
Fallback The chip couldn't be read, so the magnetic stripe was read instead.
TechnicalFallback The chip couldn't be read because of a fault in the chip or the reader, so the magnetic stripe was read instead.
EmptyCandidateFallback The chip was read, but the card and the terminal share no application, so the magnetic stripe was read instead.

Every value except Manual classifies the transaction as card-present. Leaving entryMode out stores it as Unknown, and the processor message then infers presence from the profile's industry instead of from the read. For a terminal read, that inference is the wrong place to start.

Cleartext read fields

When the reader hands you the card data in the clear, send it on these fields. The encrypted case is in Encrypted reads below.

Field Carries Required
cardData.trackData The track data from a magnetic stripe read. For UnencryptedCardReaderSwipe, unless you send emvData instead.
cardData.emvData The chip data the reader produced, as a TLV string. For Icc and Proximity.
deviceData.cvmResults The cardholder verification method the reader used: 1 for offline PIN, 2 for online PIN, 3 for signature. No. Send it when the reader reports it and the chip data has no tag 9F34. WinkPG reads it only as a fallback for that tag.

A request whose fields don't match its entry mode is rejected with HTTP 400 and a CardData: code. For example, Icc with no emvData returns CardData:EmvDataRequiredForIccOrProximity, and UnencryptedCardReaderSwipe with neither trackData nor emvData returns CardData:TrackOrEmvRequiredForUnencryptedSwipe.

Cardholder presence

cardholderPresence describes whether the cardholder is at the point of sale. That's a different question from how the card was read. Its values are CardPresent, MOTO, ECommerce, and Recurring.

Leave it unset for a magnetic stripe, chip, contactless, or fallback read. WinkPG infers CardPresent from any of those entry modes, which is the right answer.

Set it when the inference would be wrong. The common case is a keyed entry at a counter: with entryMode set to Manual, WinkPG infers MOTO, so a cardholder standing at the terminal needs cardholderPresence set to CardPresent explicitly.

cardholderPresence doesn't choose the channel the processor receives. On Fiserv and TSYS, the industry on the merchant's processor profile does: a retail profile sends card-present transactions, a direct marketing profile sends MOTO transactions, and an electronic commerce profile sends e-commerce transactions. When you set cardholderPresence to CardPresent, MOTO, or ECommerce on an authorization or a sale, and the profile sends a different channel, the transaction is processed as the profile's channel and responseData.cardholderPresenceProfileMismatch is true. It's false when the two agree, and it's absent when there's nothing to compare. A mismatch doesn't change the outcome, so if you see it, ask the merchant's administrator to check the processor profile.

Terminal capability

cardData.terminalCapability declares what the terminal hardware can read, which can differ from how this card was read. Its values are MagneticStripe, MagneticStripeWithPin, Chip, ChipWithPin, ChipContactless, and ChipContactlessWithPin.

It's optional. Leave it unset and the processor message carries the processor's default capability. Set it when the terminal is stripe-only, so the processor doesn't see a chip capability the device doesn't have. A stripe-only capability combined with a chip, contactless, or fallback entry mode is a contradiction, and it's rejected with HTTP 400 and CardData:EntryModeIncompatibleWithTerminalCapability.

Device data

The deviceData block holds two kinds of field, and they behave differently.

These three fields are telemetry. WinkPG stores them and shows them back, and they don't change how the payment is processed:

Field Carries
deviceData.serialNumber The device's serial number. This is the field that identifies the physical device; see Identifying the terminal.
deviceData.sdkVersion The version of the terminal application or the reader SDK.
deviceData.batteryLevel The battery level, as a whole-number percentage, for a device that reports one.

These two fields change processing:

Field Effect
deviceData.isEncrypted true tells WinkPG that every card data field on the request holds ciphertext, including emvData. Defaults to false.
deviceData.encryptionType The scheme the reader encrypted with. It selects the decryption path; see Encrypted reads.

[!WARNING] Setting isEncrypted to true on a cleartext read makes WinkPG treat emvData as an opaque encrypted payload instead of TLV. The chip data isn't parsed, so the card application isn't identified from it.

Example

A cleartext chip read, sent from lane 4:

curl -X POST "https://your-gateway-host/api/transactions" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionType": "Sale",
    "idempotencyKey": "lane-04-000123",
    "merchantReference": "TICKET-000123",
    "register": {
      "id": "00000000-0000-0000-0000-000000000004",
      "name": "Lane 4"
    },
    "cardData": {
      "entryMode": "Icc",
      "emvData": "EMV_TLV_FROM_THE_READER"
    },
    "deviceData": {
      "serialNumber": "DEVICE_SERIAL_NUMBER",
      "sdkVersion": "TERMINAL_APP_VERSION",
      "isEncrypted": false
    },
    "invoiceData": {
      "amounts": { "base": 10.00, "total": 10.00 }
    }
  }'

EMV_TLV_FROM_THE_READER stands for the TLV string the reader returned for this one read. Never build it by hand or reuse one from an earlier read: the chip produces a new cryptogram for each authorization.

Encrypted reads

When the reader encrypts the card data, set deviceData.isEncrypted to true, set deviceData.encryptionType to the reader's scheme, and send the key serial number the reader reports on cardData.keySerialNumber. The ciphertext goes on these fields:

  • cardData.encryptedTrack2Data, plus encryptedTrack1Data when the reader produces it. This is the only payload WinkPG decrypts itself, so every scheme it decrypts needs it, including on a chip read.
  • cardData.emvData, for an encrypted chip or contactless read. WinkPG never decrypts this field. It's forwarded as ciphertext on the schemes that forward, and ignored by the decryption on the schemes that don't.

Whether anything has to be set up first depends on the scheme. The comparison on encryptionType ignores case and surrounding spaces.

encryptionType What it needs before the first sale What WinkPG does with the read
MAGENSA Nothing on the WinkPG side. Decrypts nothing. Forwards the encrypted tracks, the encrypted chip data, and the key serial number to the decryption service in front of the processor.
PAXDUKPT The reader is injected with WinkPG's base derivation key. That's a provisioning step with the key injection facility, not an API call. Decrypts the encrypted track 2 data with that key before authorization. If the merchant is boarded on a payment encryption provider, the read is decrypted through the provider instead, on the same terms as DUKPT below.
DUKPT or ONGUARD An administrator boards the merchant on a payment encryption provider and adds a key entry for each key serial identifier the merchant's readers use. Decrypts through the provider, using the key entry the read's key serial number matches. A key serial number with no matching entry is refused with HTTP 403 and Decryption:UnknownKeySerialIdentifier, and a read with no encrypted track data or no key serial number with HTTP 400 and Decryption:InvalidPayload. Neither creates a transaction. A merchant that isn't boarded has the read forwarded to the processor still encrypted.
Any other value Nothing on the WinkPG side. Decrypts nothing. Forwards the payload to the processor as sent.

PAXDUKPT reads the key serial number from a different place than the other schemes:

  • When the merchant isn't boarded on a provider, the key serial number is read from deviceData.serialNumber and nowhere else. A read with an empty serialNumber isn't decrypted.
  • When the merchant is boarded, cardData.keySerialNumber is read first, and deviceData.serialNumber is the fallback.

Every other scheme reads cardData.keySerialNumber only, and deviceData.serialNumber stays the device's own serial number.

If the merchant is boarded on a payment encryption provider, the Encrypted card reader payments guide in the help center covers the decryption record on the response and every refusal code to handle.

Online PIN

For a PIN debit or EBT sale, send the encrypted PIN block the PIN pad produced on cardData.pin, together with the key serial number the block was encrypted under. WinkPG never decrypts or translates a PIN block. It forwards the block and its key serial number to the processor unchanged, so the PIN pad's PIN key has to be one the processor can derive.

Which field carries that key serial number depends on how the reader was injected:

Reader keys Send
One key for card data and PINs The key serial number on cardData.keySerialNumber. Leave cardData.pinKeySerialNumber out.
Separate data and PIN keys The data key's serial number on cardData.keySerialNumber, and the PIN key's on cardData.pinKeySerialNumber.

Separate keys are the usual arrangement when a payment encryption provider holds the data key and the processor owns the PIN key. keySerialNumber is the one WinkPG uses to decrypt the card data, and pinKeySerialNumber travels with the PIN block. pinKeySerialNumber is never used to decrypt anything, so it needs no key entry on the merchant's payment encryption provider.

pinKeySerialNumber is 10 to 24 hexadecimal characters, with or without the reader's leading F padding. Any other value is rejected with HTTP 400 and CardData:PinKeySerialNumberInvalid.

Healthcare (FSA and HSA) amounts

For a sale on a healthcare card, send the healthcare part of the basket on fsa, one amount per category, and set fsa.partialAuthSupport to true. A healthcare card often covers only part of the sale, and a partial approval lets that part approve. Each amount is the part of the sale in that category, not the sale total, and the healthcare total WinkPG sends is the sum of qhpAmount, rxAmount, visionAmount, dentalAmount, clinicalAmount, and copayAmount.

"fsa": {
  "rxAmount": 12.34,
  "copayAmount": 5.00,
  "partialAuthSupport": true
}

What reaches the processor depends on the processor:

Field Fiserv TSYS Chase Paymentech
Healthcare total Sent on Visa and Mastercard Sent on Visa and Mastercard Not sent
rxAmount Sent on Visa Sent Not sent
visionAmount Sent on Visa Sent Not sent
clinicalAmount Sent on Visa Sent Not sent
dentalAmount Sent on Visa Sent Not sent
copayAmount Sent on Visa and Mastercard In the total only Not sent
qhpAmount In the total only In the total only Not sent
transitAmount Not sent Not sent Not sent
partialAuthSupport Honored Honored Honored

On Fiserv, the amounts ride a sale, an authorization, and an online refund. On TSYS:

  • The amounts are sent only once healthcare amounts are enabled for your deployment. Until then, a TSYS sale carrying them is authorized for its full amount without the healthcare detail.
  • They ride a sale or an authorization from a magnetic stripe read, a keyed card, or a card-not-present payment. A chip read, a refund, a stored-credential or digital wallet payment, a 3-D Secure authenticated payment, a payment with cash back, and a card-not-present payment at a retail or restaurant merchant are sent without them.
  • At most three of rxAmount, visionAmount, clinicalAmount, and dentalAmount are itemized, taken in that order. When all four are present, dentalAmount is sent inside the total.
  • Sending the amounts states that the merchant checked the purchase against an Inventory Information Approval System (IIAS). Send them only from a point of sale that does.

transitAmount isn't healthcare spend, so it isn't part of the healthcare total, and no processor receives it. On every processor, an amount that isn't sent leaves the sale unchanged.

What comes back and what's stored

The response to the create request, and every later GET /api/transactions/{transactionId}, carries the transaction as stored. For a card-present sale, that includes:

  • cardData.entryMode, as you sent it. It also classifies the transaction under POS Terminal in the Entry Mode filter on the transactions list.
  • The whole deviceData block, the register, and the merchantReference.
  • Presence flags in place of the raw read: cardData.hasEmvData, cardData.hasTrackData, cardData.hasEncryptedTrackData, and cardData.hasPin.

The raw track data, chip data, encrypted tracks, PIN, and reader-generated dynamic verification value are never stored. They're used for the authorization and dropped, so a read can't be recovered from the transaction afterward, and the presence flags are the only record that the request carried them.

An abridged response for the example above:

{
  "id": "6f3b2c18-0a4d-4a9e-9d4f-2b71c2f0a911",
  "transactionType": "Sale",
  "merchantReference": "TICKET-000123",
  "register": {
    "id": "00000000-0000-0000-0000-000000000004",
    "name": "Lane 4"
  },
  "cardData": {
    "entryMode": "Icc",
    "hasEmvData": true,
    "hasTrackData": false,
    "hasEncryptedTrackData": false,
    "hasPin": false
  },
  "deviceData": {
    "serialNumber": "DEVICE_SERIAL_NUMBER",
    "sdkVersion": "TERMINAL_APP_VERSION",
    "isEncrypted": false
  }
}

Tokenize a terminal read

A terminal can store a card without charging it, so the merchant can charge it later. There's no single request that does this from a read:

  • POST /api/tokens/create-standalone-async takes a card number in the clear and no deviceData. It doesn't decrypt anything or read track or chip data, so it can't tokenize a read.
  • The transactions API refuses saveCardRequested, so a payment can't store its own card.

Make two calls instead:

  1. A zero-dollar Authorization that carries the read. WinkPG decrypts the read before anything else looks at the card, takes the card number and expiration date from the decrypted track, and verifies the card with the processor without holding any funds.
  2. POST /api/tokens/create-from-transaction-async with that transaction's identifier. WinkPG stores the card number it took from the read and returns a token.

Step 1: Verify the card for zero

Send the read on an ordinary create request, with transactionType set to Authorization and every amount set to zero. An encrypted read carries the same fields it carries on a sale (see Encrypted reads): deviceData.isEncrypted, deviceData.encryptionType, cardData.keySerialNumber, cardData.encryptedTrack2Data, and cardData.encryptedTrack1Data when the reader produces it. Add cardData.emvData for a chip or contactless read.

curl -X POST "https://your-gateway-host/api/transactions" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionType": "Authorization",
    "idempotencyKey": "lane-04-verify-000124",
    "merchantReference": "TICKET-000124",
    "register": {
      "id": "00000000-0000-0000-0000-000000000004",
      "name": "Lane 4"
    },
    "cardData": {
      "entryMode": "Icc",
      "emvData": "ENCRYPTED_EMV_FROM_THE_READER",
      "encryptedTrack2Data": "ENCRYPTED_TRACK2_FROM_THE_READER",
      "keySerialNumber": "KSN_FROM_THE_READER"
    },
    "deviceData": {
      "serialNumber": "DEVICE_SERIAL_NUMBER",
      "sdkVersion": "TERMINAL_APP_VERSION",
      "isEncrypted": true,
      "encryptionType": "DUKPT"
    },
    "invoiceData": {
      "amounts": { "base": 0.00, "total": 0.00 }
    }
  }'

Keep the transaction id from the response, and go on to step 2 only when responseData.resultCode reads Ok. Step 2 stores a card only from a transaction the processor approved: it refuses a declined verification, one that returned an error, or one with no result yet, with HTTP 400 and Transactions:TokenMintTransactionNotApproved, and stores nothing. Reading the result first saves that call. When the verification is declined, run a new one rather than repeating step 2.

A zero-dollar authorization holds no funds, never settles, and has nothing to void. WinkPG refuses a Void against it with VOID_NOT_APPLICABLE_ZERO_DOLLAR_VERIFICATION. It needs a processor that supports zero-dollar account verification, the same capability the Save Payment Method option on a hosted payment page needs.

On TSYS, a zero-dollar magnetic stripe or keyed read is sent to the processor as an account verification. A zero-dollar chip or contactless read is sent as an authorization for zero with the chip data.

Step 2: Create the token from the transaction

Pass the transaction's identifier and your merchant identifier on the query string. Add customerId when the card belongs to a customer.

curl -X POST "https://your-gateway-host/api/tokens/create-from-transaction-async?transactionId=TRANSACTION_ID&merchantId=MERCHANT_ID&customerId=CUSTOMER_ID" \
  -H "api-key: YOUR_API_KEY"

The response carries the token's publicReference, which is the value to send as tokenData.token on a later charge, and a masked copy of what was stored. An abridged response:

{
  "publicReference": "pt_4mRt8XwQ2nLc6ZbK9hJ3vD7s",
  "maskedPaymentDetails": {
    "cardData": {
      "cardNumber": "**** **** **** 1111"
    }
  },
  "type": "Card"
}

Reusing a stored payment method with payment tokens covers charging the stored card later.

What's stored and what isn't

The token holds the card number and expiration date WinkPG took from the read, together with the name on card, billing address, email, and phone you sent on the authorization. It never holds the track data, the chip data, the encrypted tracks, the key serial number, the PIN, or a cryptogram. Those were used for the verification and dropped, the same as on a sale.

Whether there's a card number to store depends on whether WinkPG decrypted the read:

Read Can it be tokenized?
DUKPT or ONGUARD Yes, when the merchant is boarded on a payment encryption provider. A merchant that isn't boarded has the read forwarded to the processor still encrypted, which leaves no card number to store.
PAXDUKPT Yes, when WinkPG decrypts the read. On a merchant that isn't boarded on a provider, a read that can't be decrypted is forwarded still encrypted and leaves no card number to store.
MAGENSA No. WinkPG forwards the read to the decryption service without decrypting it, so it never has the card number. The transaction holds only a masked number.
Any other encryptionType No, for the same reason: the payload is forwarded as sent.
A cleartext magnetic stripe read Yes. The card number and expiration date come from the track data.
A cleartext chip or contactless read with no track data No. WinkPG doesn't take the card number from chip data.

An authorization sent over the transactions API holds no other credential WinkPG could store in place of the card number, because the API doesn't let a payment ask to save its card. So when there's no card number, the create call is refused with HTTP 400 and Transactions:TokenMintTenderMissing, and no token is created.

A terminal can ask the cardholder, on its screen or for a signature, to let the merchant store the card and charge it later without them present. Card network rules require that agreement before a merchant-initiated charge, and WinkPG checks for it. Record it on the same create call: send the customerId of the customer the card is for, and send the agreement in the body as storedCredentialConsent. It holds when the cardholder agreed (capturedAtUtc), how (method, such as OnScreen or SignedForm), the uses they agreed to (allowedUsages), the version of the consent text the terminal showed (consentTextVersion), and your own reference to the signature or log line you kept (evidenceReference).

curl -X POST "https://your-gateway-host/api/tokens/create-from-transaction-async?transactionId=TRANSACTION_ID&merchantId=MERCHANT_ID&customerId=CUSTOMER_ID" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storedCredentialConsent": {
      "capturedAtUtc": "2026-10-14T15:30:00Z",
      "method": "OnScreen",
      "allowedUsages": "UnscheduledCOF",
      "consentTextVersion": "CONSENT_TEXT_VERSION_YOU_SHOWED",
      "evidenceReference": "terminal-7-sig-000481"
    }
  }'

The card is stored on the customer, the consent is recorded against it, and the response also carries the storedCredentialConsentId. Recording a consent needs the StoredCredentialConsents.Create permission as well as the token permission. Keep the signature image or recording on your own system, and send only your reference to it. The consent appears in the back office under Stored Credential Consents, captured via Api, with your reference as its Merchant Reference.

If you create the token without a consent, the card has none until someone records one. An operator does that in the back office, from the Stored Credential Consents section of the customer, and until then WinkPG refuses a merchant-initiated charge against the card. The Stored credential consents guide in the help center covers the other ways a consent is captured, and how to revoke one.

Completing a chip transaction

After an online chip or contactless authorization, the card expects the issuer's answer before it produces its final cryptogram. WinkPG returns that answer on the create response as responseData.emv, on an approval and on a decline. Pass it to the terminal's EMV kernel to complete the transaction with the card.

Field Carries
responseData.emv.issuerAuthenticationData Tag 91, the value only. The card uses it to authenticate the issuer's response.
responseData.emv.issuerScriptTemplate1 Every tag 71 template the issuer sent, each with its tag and length, in the order received. The kernel runs these before the final GENERATE AC.
responseData.emv.issuerScriptTemplate2 Every tag 72 template the issuer sent, each with its tag and length, in the order received. The kernel runs these after the final GENERATE AC.
responseData.emv.authorizationResponseCode Tag 8A, the value only. An approval reads 3030.
responseData.emv.rawTlv The whole EMV response the processor returned, including any tag not broken out above.

Every value is uppercase hexadecimal, and any field the issuer didn't send is left out. A decline can still carry issuer scripts, such as an instruction to block the card, so read responseData.emv on every outcome, not only on an approval.

responseData.emv is absent when the processor returned no EMV response data, which is the case for every read that isn't chip or contactless. On TSYS, it's also absent unless the merchant's processor profile sets EMV Response Data Support to Supported. Whoever boards the merchant sets it once, on the profile.

A later GET /api/transactions/{transactionId} returns the same block, until a void or reversal replaces the response data with its own. Webhooks don't carry it. Read it from the create response, which is when the terminal needs it.

An abridged decline that carries an issuer script:

{
  "responseData": {
    "resultCode": "Decline",
    "emv": {
      "issuerAuthenticationData": "1A2B3C4D5E6F70810012",
      "issuerScriptTemplate1": "71169F180411223344860D8424000008AABBCCDDEEFF0011",
      "authorizationResponseCode": "3035",
      "rawTlv": "8A023035910A1A2B3C4D5E6F7081001271169F180411223344860D8424000008AABBCCDDEEFF0011"
    }
  }
}

The values above are illustrative. Pass the kernel exactly what the response carries, and never reuse a value from an earlier transaction.

When the card declines after an online approval

The issuer can approve a chip authorization online and the card can still decline the transaction at the final GENERATE AC, for example when the card rejects the issuer's authentication data. The sale didn't complete, but the issuer is holding the funds. Release the hold with a full Reversal on the operations endpoint, with reversalReasonCode set to ChipDeclinedAfterOnlineApproval:

curl -X POST \
  "https://your-gateway-host/api/transactions/by-merchant/{merchantId}/{transactionId}/operations" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operationType": "Reversal",
    "reversalReasonCode": "ChipDeclinedAfterOnlineApproval",
    "reason": "Card declined at the final GENERATE AC"
  }'

Leave out amount, so the whole authorization is released. The reversal carries no chip data, and the operations endpoint takes none: the processor finds the original authorization by its identifiers.

How the processor hears the reason depends on the processor:

Processor What the reversal states
TSYS Message reason code 2517, chip declined after online issuer approval.
Chase Paymentech The card-declined reversal reason.
Fiserv An ordinary void. Fiserv defines no reason for this case.

The transaction history records the reason on every processor. For the other reason codes, see Refunds, voids, and reversals.

When the terminal can't reach WinkPG

WinkPG has no store-and-forward submission. There's no request that carries a sale the card approved offline, with its chip data and offline approval code, for the gateway to settle later. Configure the terminal to authorize online only, and to decline when it can't reach WinkPG, rather than approving under a floor limit.

A Force with originalTransaction.authCode isn't a substitute. It's built for a voice authorization the merchant obtained by phone, not for a card read, and it isn't processed as a chip, PIN debit, or healthcare (FSA or HSA) sale. Don't send cardData.pin or any non-zero fsa amount on a Force. Depending on the deployment, a Force that carries either is refused with HTTP 400 and the code Transactions:ForceTenderNotSupported. Submit a PIN debit or healthcare sale online while the cardholder is present.

Identifying the terminal

Three native fields identify where a transaction came from. Each answers a different question, and you can send all three on the same request.

Field Identifies Where you see it
deviceData.serialNumber The physical device. Returned on GET and on lifecycle webhooks. Shown as Serial Number in the Device section of the transaction's detail page.
register.id and register.name The lane, till, or station, independent of which device is in it today. Returned on GET and on lifecycle webhooks. Receipts print the name after TID:.
merchantReference The merchant's own ticket, order, or invoice number. Returned on GET and on lifecycle webhooks. It's also carried to the acquirer on the clearing record, so it's the value to reconcile against on the acquirer's transaction report.

Keep merchantReference free of card numbers and other sensitive data, because it's stored on the transaction and sent to the acquirer.

For a PAXDUKPT reader on a merchant that isn't boarded, deviceData.serialNumber carries the key serial number (see Encrypted reads), so use register to identify the terminal instead.

Custom fields are a fourth option, for any other value you want to search or report on. The field has to be defined and turned on for the merchant first: once a merchant has at least one custom field turned on, a create request naming a field that isn't one of them is rejected with HTTP 400. See Transaction custom fields for how to define them.

Webhooks

Every transaction lifecycle event fires for a card-present transaction exactly as it does for any other. WinkPG doesn't filter events by source or entry mode, so a subscription that receives Transaction.Authorized for an online payment receives it for a tap at lane 4 too.

The lifecycle event body carries the transaction identifier, the merchant identifier, the status, the amounts, the result and decline reason codes, and on Transaction.Settled the batch identifiers. It also carries the values that attribute the transaction to a terminal and to your own sale. They use the same names and nesting as GET /api/transactions/{transactionId}, written in the event body's PascalCase, so a case-insensitive parser reads both:

Field in the event body Field on GET Carries
IdempotencyKey idempotencyKey The idempotency key the transaction was created with. For a terminal integration, this is usually your own sale ID.
MerchantReference merchantReference The merchant's own ticket, order, or invoice number.
DeviceData.SerialNumber deviceData.serialNumber The device serial number. In the event body, DeviceData holds the serial number only.
Register.Id and Register.Name register.id and register.name The lane, till, or station.

Each one is null when the transaction was created without it, and Register is null when the transaction has no register. Transaction.PartiallyApproved carries the same four fields.

To attribute an event to a terminal, read these fields from the event body. You don't need to read the transaction back. Read it with GET /api/transactions/{transactionId} only when you need something the event doesn't carry. See Webhook integration for the envelope, the signature, and retry behavior.

Receipts

The terminal owns the point-of-sale receipt. Print it from the create response the way your device prints any receipt: WinkPG doesn't send anything to the terminal's printer.

WinkPG also keeps its own receipt for the transaction, and you can read it over the API to print or reprint it. Read it as JSON to lay out your own printout, or as an HTML document ready to print. The receipt is generated in the background shortly after the sale, so a terminal that prints straight away asks for it with POST /api/transactions/by-merchant/{merchantId}/{transactionId}/receipts and receiptType set to Authorization, which returns the receipt and generates it first if it doesn't exist yet. Either way, the receipt shows the card's brand and last four digits only. See Transaction receipts over the API for the requests.

Refunds, voids, and reversals

A card-present sale is corrected through the same operations as any other transaction, against its transaction identifier. Nothing about the follow-up depends on the terminal. See Refunds, voids, and reversals for which operation applies at which point in the transaction's life.

What to log, and what not to

Never log track data, chip data, the PIN, or any cryptogram, in cleartext or encrypted form. They're card data, and the rules that govern a card number govern them too.

Log the transaction identifier, the entry mode, and the device serial number. Together they answer most support questions about a terminal sale: which transaction it was, how the card was read, and which device read it.

See also

Reconnecting to the server

Could not reconnect

This session has ended

Attempt 1

Your work on this page is still here. Retrying keeps it; reloading starts the page again.

The server no longer holds this page's state, so it has to be loaded again.