Restaurant POS Integration Guide
Complete integration reference for restaurant merchants using the NCR PMI REST API — covering dine-in, bar tab, tip adjustment, split tender, and gift card operations.
Restaurant payment flows are fundamentally different from retail POS. A guest may sit at a table for an hour before paying, split the bill across multiple seats, add a tip after the card has been authorized, or pay part with a gift card and the rest with a credit card. This guide covers every scenario your restaurant POS needs to handle, with working API request/response examples for each.
Retail POS: one transaction per item scan. Restaurant POS: session spans from order-entry to final payment, often 30–90 minutes later, with post-authorization adjustments (tip, incremental auth, split tender) happening in between.
Onboarding & prerequisites
Before writing integration code, NCR Voyix works with you to provision a lab environment using the Azure RLSG-CFR platform. This environment is configured end-to-end during the customer onboarding process and includes all credentials, device configuration, and host connectivity needed for end-to-end transaction testing.
Onboarding package
Your onboarding package is assembled based on your business profile. It covers payment host connectivity, PIN pad device configuration, platform support, and repository access.
Supported payment hosts
Your environment is provisioned against one or more of the following payment processors:
- Chase
- Corpay / Comdata
- Worldpay
- Wex
- Voyix Pay
- Additional hosts as applicable
Supported PIN pad devices
| Device | Supported connection |
|---|---|
| Ingenico Lane/8000 | TCP/IP, USB |
| Moby 5500 | TCP/IP, USB |
| Verifone M425 Neo / M400 | TCP/IP, USB |
Platform support
CCL and PMIWebServer are distributed for the following operating systems:
- Windows
- Linux
- Android
Product feature enablement
Features are enabled based on your business segment during onboarding:
- Hospitality — restaurant, bar, and table-service scenarios
- Retail — Grocery
- Retail — Fuel
- Commercial Fuel
- Additional features as applicable to your use case
SDK build & JFrog access
Your onboarding package specifies the CCL build version for your deployment (for example, v26.6.1) along with JFrog Artifactory credentials for downloading SDK packages and build artifacts.
SDK packages are hosted under npg-ccl-generic-releases/CCL/26.6/ on NCR's JFrog Artifactory. Your onboarding team provides the access URL and credentials.
Configuration details
In addition to environment setup, your onboarding package includes merchant configuration for the following components:
DataManager & ConMan
Your CCL configuration is provisioned with the identifiers required for every API call:
| Parameter | Description |
|---|---|
CompanyNumber |
Merchant organization identifier. Included in every request body under ConfigOptions. |
StoreNumber |
Store location identifier. Scoped to a single physical site. |
LaneNumber |
POS terminal / register number within the store. |
MID (Merchant ID) |
Host-specific identifier assigned by the payment processor for your merchant account. |
TID (Terminal ID) |
Terminal-level identifier assigned per lane by the payment host. |
Store-level and lane-level configurations are managed in DataManager. ConMan handles configuration delivery to the CCL engine at Initialize and OpenLane time.
The MID and TID values provided in your lab onboarding package are for test use only. Contact your NCR account team to obtain production values before go-live.
Before you begin
Ensure the following are in place before writing integration code:
- CCL 26.6 SDK installed and
PMIWebServerrunning onhttps://localhost:8600 - Merchant credentials: Company Number, Store Number, Lane Number, Client ID
- Lane initialized and open:
GetEngineStatus→Initialize(if needed) →OpenLane. See the Retail PMI guide for startup details. - TLS certificate: PMIWebServer uses a self-signed cert. In non-production, disable cert verification. In production, install the NCR-provided cert.
Every BeginPaymentSession must be paired with EndPaymentSession — even on error. Leaving sessions open will block subsequent payments. Use try/finally or equivalent in your implementation.
Restaurant payment use cases
The following table maps every scenario to the PMI API calls required:
| Scenario | PMI APIs involved | Section |
|---|---|---|
| Standard dine-in Guest pays full check at end of meal |
BeginPaymentSession → GetCardDetails → Purchase → EndPaymentSession |
Standard payment |
| Bar tab Card stored on file; charged when guest closes out |
BeginPaymentSession → OpenTab → … → BeginPaymentSession → CloseTab (+ optional IncrementalAuth) |
Bar tab |
| Tip adjustment Tip added after authorization (server-entered) |
BeginPaymentSession → Adjustment → EndPaymentSession |
Tip adjustment |
| Device tip prompt Guest selects tip % on PIN pad before authorization |
GetCardDetails with PromptTip: "YES" → Purchase with total + tip |
Device tip |
| Split tender Multiple payments on one check (e.g., gift card + card) |
Multiple sequential BeginPaymentSession → Purchase cycles, each for a partial amount |
Split tender |
| Gift card as tender Use gift card balance to pay (fully or partially) |
BeginPaymentSession → GetCardDetails (CardType: GiftCard) → Purchase (isGiftCard) → EndPaymentSession |
Gift card tender |
| Gift card management Activate / Reload / Deactivate / CashOut / BalanceInquiry |
BeginPaymentSession → Activate | Reload | Deactivate | CashOut | BalanceInquiry → EndPaymentSession |
Gift card mgmt |
Payment flow diagrams
Standard dine-in payment
Bar tab — OpenTab → CloseTab
Session lifecycle
Every PMI payment operation is wrapped in a session. The session ID (SessionTranID) returned by BeginPaymentSession must be included in all subsequent calls within that session.
BeginPaymentSession
Open a session. Receive SessionTranID. Required before any payment operation.
Payment Operation
GetCardDetails, Purchase, OpenTab, CloseTab, Adjustment, Activate, etc.
EndPaymentSession
Always close the session. Required even on errors or cancellations.
Every BeginPaymentSession must be followed by EndPaymentSession — even on error or cancellation. A session left open will block all further payment operations on that lane.
Standard dine-in payment
The most common restaurant flow. The server presents the check, the guest taps/inserts their card, and the full amount is captured in one transaction.
Open a payment session
curl -X POST https://localhost:8600/PMI/BeginPaymentSession \
-H "Content-Type: application/json" \
-H "X-ClientId: POS1" \
--insecure \
-d '{
"Message": {
"ClientID": "POS1",
"MessageType": "Request",
"Operation": "BeginPaymentSession",
"RequestID": "1001",
"Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
}
}'
# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "SessionTranID": "ccl-session-001" } } }// Node.js 18+ (built-in fetch). Set NODE_TLS_REJECT_UNAUTHORIZED=0 for self-signed cert.
const response = await fetch('https://localhost:8600/PMI/BeginPaymentSession', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({
Message: {
ClientID: 'POS1', MessageType: 'Request',
Operation: 'BeginPaymentSession', RequestID: '1001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
}
})
});
const { Message } = await response.json();
const sessionTranID = Message.PaymentDetails.SessionTranID;import requests
resp = requests.post(
'https://localhost:8600/PMI/BeginPaymentSession',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={
'Message': {
'ClientID': 'POS1', 'MessageType': 'Request',
'Operation': 'BeginPaymentSession', 'RequestID': '1001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}
}
},
verify=False # self-signed cert on localhost
)
msg = resp.json()['Message']
session_tran_id = msg['PaymentDetails']['SessionTranID'] [
'ClientID' => 'POS1',
'MessageType' => 'Request',
'Operation' => 'BeginPaymentSession',
'RequestID' => '1001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/BeginPaymentSession');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$sessionTranID = $msg['PaymentDetails']['SessionTranID'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
MessageType = "Request",
Operation = "BeginPaymentSession",
RequestID = "1001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/BeginPaymentSession", payload);
var root = await resp.Content.ReadFromJsonAsync();
var sessionTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("SessionTranID").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/BeginPaymentSession')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
MessageType: 'Request',
Operation: 'BeginPaymentSession',
RequestID: '1001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
session_tran_id = msg['PaymentDetails']['SessionTranID']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","MessageType":"Request","Operation":"BeginPaymentSession","RequestID":"1001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/BeginPaymentSession"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.SessionTranID package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"MessageType": "Request",
"Operation": "BeginPaymentSession",
"RequestID": "1001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/BeginPaymentSession", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["SessionTranID"]}Start notifications and get card details
Call StartNotifications first to activate the PIN pad display, then call GetCardDetails which waits for the customer to present their card.
curl -X POST https://localhost:8600/PMI/GetCardDetails \
-H "Content-Type: application/json" \
-H "X-ClientId: POS1" \
--insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "GetCardDetails",
"RequestID": "1002", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
"PaymentDetails": { "SessionTranID": "ccl-session-001" },
"CardDetails": [{ "CardType": "Credit" }]
}
}'
# Response
# { "Message": { "StatusCode": 0,
# "PaymentDetails": { "PaymentTranID": "pmt-tran-abc123" },
# "CardDetails": [{ "CardBrand": "Visa", "LastFour": "4242", "EntryMethod": "Tap" }] } }const response = await fetch('https://localhost:8600/PMI/GetCardDetails', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({
Message: {
ClientID: 'POS1', Operation: 'GetCardDetails',
RequestID: '1002', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
PaymentDetails: { SessionTranID: sessionTranID },
CardDetails: [{ CardType: 'Credit' }]
}
})
});
const { Message } = await response.json();
const paymentTranID = Message.PaymentDetails.PaymentTranID;
const { CardBrand, LastFour } = Message.CardDetails[0];resp = requests.post(
'https://localhost:8600/PMI/GetCardDetails',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={
'Message': {
'ClientID': 'POS1', 'Operation': 'GetCardDetails',
'RequestID': '1002', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
'PaymentDetails': {'SessionTranID': session_tran_id},
'CardDetails': [{'CardType': 'Credit'}]
}
}, verify=False
)
msg = resp.json()['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID'] [
'ClientID' => 'POS1',
'Operation' => 'GetCardDetails',
'RequestID' => '1002',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '52.75',
'AmountTendered' => '52.75'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-001'
],
'CardDetails' => [[
'CardType' => 'Credit'
]]
]
]);
$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$paymentTranID = $msg['PaymentDetails']['PaymentTranID'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "GetCardDetails",
RequestID = "1002",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "52.75",
AmountTendered = "52.75"
},
PaymentDetails = new {
SessionTranID = "ccl-session-001"
},
CardDetails = new[] {{'CardType': 'Credit'}}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var paymentTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("PaymentTranID").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'GetCardDetails',
RequestID: '1002',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '52.75',
AmountTendered: '52.75'
},
PaymentDetails: {
SessionTranID: 'ccl-session-001'
},
CardDetails: [{'CardType': 'Credit'}]
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"1002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-001"},"CardDetails":[{"CardType":"Credit"}]}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.PaymentTranID package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "GetCardDetails",
"RequestID": "1002",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "52.75",
"AmountTendered": "52.75",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-001",
},
"CardDetails": []any{{'CardType': 'Credit'}},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["PaymentTranID"]}Authorize and capture — Purchase
curl -X POST https://localhost:8600/PMI/Purchase \
-H "Content-Type: application/json" \
-H "X-ClientId: POS1" \
--insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Purchase",
"RequestID": "1003", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
"PaymentDetails": {
"SessionTranID": "ccl-session-001",
"PaymentTranID": "pmt-tran-abc123"
}
}
}'
# Response
# { "Message": { "StatusCode": 0,
# "PaymentDetails": { "AuthCode": "AUTH123456", "ResponseMessage": "APPROVED" },
# "HostData": { "ReferenceID": "ref-host-789xyz" },
# "Amounts": { "AmountApproved": "52.75" } } }const response = await fetch('https://localhost:8600/PMI/Purchase', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({
Message: {
ClientID: 'POS1', Operation: 'Purchase',
RequestID: '1003', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
PaymentDetails: { SessionTranID: sessionTranID, PaymentTranID: paymentTranID }
}
})
});
const { Message } = await response.json();
// ⚠️ Save these — needed for Adjustment later
const authCode = Message.PaymentDetails.AuthCode;
const referenceID = Message.HostData.ReferenceID;resp = requests.post(
'https://localhost:8600/PMI/Purchase',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={
'Message': {
'ClientID': 'POS1', 'Operation': 'Purchase',
'RequestID': '1003', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
'PaymentDetails': {'SessionTranID': session_tran_id, 'PaymentTranID': payment_tran_id}
}
}, verify=False
)
msg = resp.json()['Message']
auth_code = msg['PaymentDetails']['AuthCode']
reference_id = msg['HostData']['ReferenceID'] # Save for Adjustment [
'ClientID' => 'POS1',
'Operation' => 'Purchase',
'RequestID' => '1003',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '52.75',
'AmountTendered' => '52.75'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-001',
'PaymentTranID' => 'pmt-tran-abc123'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/Purchase');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "Purchase",
RequestID = "1003",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "52.75",
AmountTendered = "52.75"
},
PaymentDetails = new {
SessionTranID = "ccl-session-001",
PaymentTranID = "pmt-tran-abc123"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Purchase", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/Purchase')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'Purchase',
RequestID: '1003',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '52.75',
AmountTendered: '52.75'
},
PaymentDetails: {
SessionTranID: 'ccl-session-001',
PaymentTranID: 'pmt-tran-abc123'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Purchase","RequestID":"1003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-001","PaymentTranID":"pmt-tran-abc123"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/Purchase"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode + Message.HostData.ReferenceID package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "Purchase",
"RequestID": "1003",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "52.75",
"AmountTendered": "52.75",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-001",
"PaymentTranID": "pmt-tran-abc123",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Purchase", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}You must save ReferenceID from HostData and PaymentTranID from PaymentDetails to your database. Both are required if the server adds a tip via Adjustment later.
End the session
curl -X POST https://localhost:8600/PMI/EndPaymentSession \
-H "Content-Type: application/json" \
-H "X-ClientId: POS1" \
--insecure \
-d '{"Message":{"ClientID":"POS1","Operation":"EndPaymentSession",
"RequestID":"1004","Version":"4.7.0",
"PaymentDetails":{"SessionTranID":"ccl-session-001"}}}'await fetch('https://localhost:8600/PMI/EndPaymentSession', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'EndPaymentSession',
RequestID: '1004', Version: '4.7.0',
PaymentDetails: { SessionTranID: sessionTranID }
}})
});requests.post(
'https://localhost:8600/PMI/EndPaymentSession',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'EndPaymentSession',
'RequestID': '1004', 'Version': '4.7.0',
'PaymentDetails': {'SessionTranID': session_tran_id}}},
verify=False
) [
'ClientID' => 'POS1',
'Operation' => 'EndPaymentSession',
'RequestID' => '1004',
'Version' => '4.7.0',
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-001'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/EndPaymentSession');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "EndPaymentSession",
RequestID = "1004",
Version = "4.7.0",
PaymentDetails = new {
SessionTranID = "ccl-session-001"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/EndPaymentSession", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/EndPaymentSession')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'EndPaymentSession',
RequestID: '1004',
Version: '4.7.0',
PaymentDetails: {
SessionTranID: 'ccl-session-001'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"EndPaymentSession","RequestID":"1004","Version":"4.7.0","PaymentDetails":{"SessionTranID":"ccl-session-001"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/EndPaymentSession"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success" package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "EndPaymentSession",
"RequestID": "1004",
"Version": "4.7.0",
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-001",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/EndPaymentSession", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Status"]}Bar tab — OpenTab / CloseTab
The bar tab flow captures the card at arrival and stores it server-side. No charge occurs at OpenTab time. When the guest is ready to leave, CloseTab retrieves the stored card and charges the final amount including tip — no second card tap needed.
Step 1 — Open the tab (card capture, no charge)
# After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
curl -X POST https://localhost:8600/PMI/OpenTab \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "OpenTab",
"RequestID": "2001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"PaymentDetails": { "SessionTranID": "ccl-session-bar-001" },
"PosTranID": "pos-tran-001"
}
}'
# Response → Message.HostData.ReferenceID + Message.PaymentDetails.PaymentTranID ⚠️ Save both for CloseTab// After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
const response = await fetch('https://localhost:8600/PMI/OpenTab', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'OpenTab', RequestID: '2001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
PaymentDetails: { SessionTranID: sessionTranID },
PosTranID: 'pos-tran-001'
}})
});
const { Message } = await response.json();
const tabReferenceID = Message.HostData.ReferenceID; // ⚠️ Save for CloseTab
const tabPaymentTranID = Message.PaymentDetails.PaymentTranID; // ⚠️ Save for CloseTab# After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
resp = requests.post(
'https://localhost:8600/PMI/OpenTab',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'OpenTab',
'RequestID': '2001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'PaymentDetails': {'SessionTranID': session_tran_id},
'PosTranID': 'pos-tran-001'}},
verify=False
)
msg = resp.json()['Message']
tab_reference_id = msg['HostData']['ReferenceID'] # Save for CloseTab
tab_payment_tran = msg['PaymentDetails']['PaymentTranID'] # Save for CloseTab [
'ClientID' => 'POS1',
'Operation' => 'OpenTab',
'RequestID' => '2001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-bar-001'
],
'PosTranID' => 'pos-tran-001'
]
]);
$ch = curl_init('https://localhost:8600/PMI/OpenTab');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$tabRef = $msg['HostData']['ReferenceID']; // save for CloseTabusing System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "OpenTab",
RequestID = "2001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
PaymentDetails = new {
SessionTranID = "ccl-session-bar-001"
},
PosTranID = "pos-tran-001"
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/OpenTab", payload);
var root = await resp.Content.ReadFromJsonAsync();
var tabRef = root.GetProperty("Message").GetProperty("HostData").GetProperty("ReferenceID").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/OpenTab')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'OpenTab',
RequestID: '2001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
PaymentDetails: {
SessionTranID: 'ccl-session-bar-001'
},
PosTranID: 'pos-tran-001'
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
tab_ref = msg['HostData']['ReferenceID'] # save for CloseTab
tab_payment_tran = msg['PaymentDetails']['PaymentTranID'] # save for CloseTabimport java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"OpenTab","RequestID":"2001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-session-bar-001"},"PosTranID":"pos-tran-001"}}"""; // Response: HostData.ReferenceID + PaymentDetails.PaymentTranID — save both for CloseTab
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/OpenTab"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.HostData.ReferenceID — save for CloseTab package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "OpenTab",
"RequestID": "2001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-bar-001",
},
"PosTranID": "pos-tran-001",
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/OpenTab", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["HostData"].(map[string]any)["ReferenceID"]}After OpenTab, save referenceId and paymentTranId against the table/guest record. You will need both to call CloseTab later — potentially after a lane restart or server shift change.
Step 2 — Close the tab (charge stored card)
# New BeginPaymentSession first (new SessionTranID), then:
curl -X POST https://localhost:8600/PMI/CloseTab \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "CloseTab",
"RequestID": "2010", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "87.50", "AmountTendered": "87.50", "TipAmount": "15.00" },
"HostData": { "ReferenceID": "tab-ref-xyz789" },
"PaymentDetails": { "SessionTranID": "ccl-session-bar-002", "PaymentTranID": "pmt-tran-bar-abc" },
"PosOptions": { "AllowAdjustment": false }
}
}'// New BeginPaymentSession first, then:
const response = await fetch('https://localhost:8600/PMI/CloseTab', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'CloseTab', RequestID: '2010', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: finalAmount, AmountTendered: finalAmount, TipAmount: tipAmount },
HostData: { ReferenceID: tabReferenceID }, // from OpenTab
PaymentDetails: { SessionTranID: newSessionID, PaymentTranID: tabPaymentTranID },
PosOptions: { AllowAdjustment: false }
}})
});
const { Message } = await response.json();resp = requests.post(
'https://localhost:8600/PMI/CloseTab',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'CloseTab',
'RequestID': '2010', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': final_amount, 'AmountTendered': final_amount, 'TipAmount': tip},
'HostData': {'ReferenceID': tab_reference_id},
'PaymentDetails': {'SessionTranID': new_session_id, 'PaymentTranID': tab_payment_tran_id},
'PosOptions': {'AllowAdjustment': False}}},
verify=False
) [
'ClientID' => 'POS1',
'Operation' => 'CloseTab',
'RequestID' => '2010',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '87.50',
'AmountTendered' => '87.50',
'TipAmount' => '15.00'
],
'HostData' => [
'ReferenceID' => 'tab-ref-xyz789'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-bar-002',
'PaymentTranID' => 'pmt-tran-bar-abc'
],
'PosOptions' => [
'AllowAdjustment' => false
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/CloseTab');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "CloseTab",
RequestID = "2010",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "87.50",
AmountTendered = "87.50",
TipAmount = "15.00"
},
HostData = new {
ReferenceID = "tab-ref-xyz789"
},
PaymentDetails = new {
SessionTranID = "ccl-session-bar-002",
PaymentTranID = "pmt-tran-bar-abc"
},
PosOptions = new {
AllowAdjustment = false
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/CloseTab", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/CloseTab')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'CloseTab',
RequestID: '2010',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '87.50',
AmountTendered: '87.50',
TipAmount: '15.00'
},
HostData: {
ReferenceID: 'tab-ref-xyz789'
},
PaymentDetails: {
SessionTranID: 'ccl-session-bar-002',
PaymentTranID: 'pmt-tran-bar-abc'
},
PosOptions: {
AllowAdjustment: false
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"CloseTab","RequestID":"2010","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"87.50","AmountTendered":"87.50","TipAmount":"15.00"},"HostData":{"ReferenceID":"tab-ref-xyz789"},"PaymentDetails":{"SessionTranID":"ccl-session-bar-002","PaymentTranID":"pmt-tran-bar-abc"},"PosOptions":{"AllowAdjustment":false}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/CloseTab"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "CloseTab",
"RequestID": "2010",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "87.50",
"AmountTendered": "87.50",
"TipAmount": "15.00",
},
"HostData": map[string]any{
"ReferenceID": "tab-ref-xyz789",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-bar-002",
"PaymentTranID": "pmt-tran-bar-abc",
},
"PosOptions": map[string]any{
"AllowAdjustment": false,
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/CloseTab", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}Tip adjustment (post-authorization)
The guest signs the receipt and writes in a tip. The server enters the tip amount into the POS, which calls Adjustment to update the authorized amount. This is the most common tip flow for full-service restaurants.
Important: Adjustment requires the ReferenceID from the original Purchase response and the original PaymentTranID. Both must be stored after the initial payment.
# After a new BeginPaymentSession:
curl -X POST https://localhost:8600/PMI/Adjustment \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Adjustment",
"RequestID": "3001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "62.75", "AmountTendered": "62.75", "TipAmount": "10.00" },
"PaymentDetails": { "SessionTranID": "ccl-session-adj-001", "PaymentTranID": "pmt-tran-abc123" },
"AdjustmentData": { "OriginalReferenceId": "ref-host-789xyz" }
}
}'// After a new BeginPaymentSession:
const response = await fetch('https://localhost:8600/PMI/Adjustment', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'Adjustment', RequestID: '3001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: totalWithTip, AmountTendered: totalWithTip, TipAmount: tip },
PaymentDetails: { SessionTranID: adjSessionID, PaymentTranID: origPaymentTranID },
AdjustmentData: { OriginalReferenceId: origReferenceID }
}})
});
const { Message } = await response.json();resp = requests.post(
'https://localhost:8600/PMI/Adjustment',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'Adjustment',
'RequestID': '3001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': total_with_tip, 'AmountTendered': total_with_tip, 'TipAmount': tip},
'PaymentDetails': {'SessionTranID': adj_session_id, 'PaymentTranID': orig_payment_tran_id},
'AdjustmentData': {'OriginalReferenceId': orig_reference_id}}},
verify=False
) [
'ClientID' => 'POS1',
'Operation' => 'Adjustment',
'RequestID' => '3001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '62.75',
'AmountTendered' => '62.75',
'TipAmount' => '10.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-adj-001',
'PaymentTranID' => 'pmt-tran-abc123'
],
'AdjustmentData' => [
'OriginalReferenceId' => 'ref-host-789xyz'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/Adjustment');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "Adjustment",
RequestID = "3001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "62.75",
AmountTendered = "62.75",
TipAmount = "10.00"
},
PaymentDetails = new {
SessionTranID = "ccl-session-adj-001",
PaymentTranID = "pmt-tran-abc123"
},
AdjustmentData = new {
OriginalReferenceId = "ref-host-789xyz"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Adjustment", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/Adjustment')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'Adjustment',
RequestID: '3001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '62.75',
AmountTendered: '62.75',
TipAmount: '10.00'
},
PaymentDetails: {
SessionTranID: 'ccl-session-adj-001',
PaymentTranID: 'pmt-tran-abc123'
},
AdjustmentData: {
OriginalReferenceId: 'ref-host-789xyz'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Adjustment","RequestID":"3001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"62.75","AmountTendered":"62.75","TipAmount":"10.00"},"PaymentDetails":{"SessionTranID":"ccl-session-adj-001","PaymentTranID":"pmt-tran-abc123"},"AdjustmentData":{"OriginalReferenceId":"ref-host-789xyz"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/Adjustment"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "Adjustment",
"RequestID": "3001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "62.75",
"AmountTendered": "62.75",
"TipAmount": "10.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-adj-001",
"PaymentTranID": "pmt-tran-abc123",
},
"AdjustmentData": map[string]any{
"OriginalReferenceId": "ref-host-789xyz",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Adjustment", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}| Field | Source | Notes |
|---|---|---|
AmountDue | original total + tip | Full final amount charged to card |
TipAmount | server input | Tip only (not included in AmountDue — added to it) |
PaymentTranID | original Purchase response | Must match the original transaction |
OriginalReferenceId | original Purchase HostData.ReferenceID | Gateway identifier for the original auth |
SessionTranID | new BeginPaymentSession | Each Adjustment needs a fresh session |
Device tip prompt
Instead of adding the tip post-authorization via Adjustment, you can prompt the guest to choose a tip percentage directly on the PIN pad during GetCardDetails. The PIN pad displays tip options (15%, 18%, 20%, Custom) and returns the total amount including tip.
curl -X POST https://localhost:8600/PMI/GetCardDetails \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "GetCardDetails",
"RequestID": "4001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
"PaymentDetails": { "SessionTranID": "ccl-session-tip-001", "PromptTip": "YES" },
"CardDetails": [{ "CardType": "Credit" }]
}
}'
# Response → Message.Amounts.TipAmount (guest-selected), .AmountTendered (total incl. tip)const response = await fetch('https://localhost:8600/PMI/GetCardDetails', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'GetCardDetails', RequestID: '4001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
PaymentDetails: { SessionTranID: sessionTranID, PromptTip: 'YES' },
CardDetails: [{ CardType: 'Credit' }]
}})
});
const { Message } = await response.json();
const tip = Message.Amounts.TipAmount; // guest-selected tip
const total = Message.Amounts.AmountTendered; // total incl. tip → pass to Purchaseresp = requests.post(
'https://localhost:8600/PMI/GetCardDetails',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'GetCardDetails',
'RequestID': '4001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
'PaymentDetails': {'SessionTranID': session_tran_id, 'PromptTip': 'YES'},
'CardDetails': [{'CardType': 'Credit'}]}},
verify=False
)
msg = resp.json()['Message']
tip = msg['Amounts']['TipAmount']
total = msg['Amounts']['AmountTendered'] [
'ClientID' => 'POS1',
'Operation' => 'GetCardDetails',
'RequestID' => '4001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '52.75',
'AmountTendered' => '52.75'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-session-tip-001',
'PromptTip' => 'YES'
],
'CardDetails' => [[
'CardType' => 'Credit'
]]
]
]);
$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$tip = $msg['Amounts']['TipAmount'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "GetCardDetails",
RequestID = "4001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "52.75",
AmountTendered = "52.75"
},
PaymentDetails = new {
SessionTranID = "ccl-session-tip-001",
PromptTip = "YES"
},
CardDetails = new[] {{'CardType': 'Credit'}}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var tip = root.GetProperty("Message").GetProperty("Amounts").GetProperty("TipAmount").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'GetCardDetails',
RequestID: '4001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '52.75',
AmountTendered: '52.75'
},
PaymentDetails: {
SessionTranID: 'ccl-session-tip-001',
PromptTip: 'YES'
},
CardDetails: [{'CardType': 'Credit'}]
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
tip = msg['Amounts']['TipAmount']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"4001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-tip-001","PromptTip":"YES"},"CardDetails":[{"CardType":"Credit"}]}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.TipAmount (guest-selected tip) package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "GetCardDetails",
"RequestID": "4001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "52.75",
"AmountTendered": "52.75",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-session-tip-001",
"PromptTip": "YES",
},
"CardDetails": []any{{'CardType': 'Credit'}},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["TipAmount"]}Device tip (PromptTip): Guest selects tip on PIN pad before the card is charged. Cleaner UX, no second API call needed. Best for counter service or quick-service restaurants.
Adjustment: Server adds tip after authorization, usually when the guest signs a paper slip and writes in a tip. Best for full-service dine-in where the check is left on the table.
Split tender
A guest pays part of the check with a gift card and the rest with a credit card. Each payment is a separate BeginPaymentSession → GetCardDetails → Purchase → EndPaymentSession cycle. Your POS tracks the remaining balance and presents the next payment form.
Each tender in a split payment is a fully independent PMI transaction. Run them sequentially:
- Payment 1 —
BeginPaymentSession→GetCardDetails(CardType: GiftCard) →Purchase(AmountDue: gift card portion) →EndPaymentSession.
ReadAmounts.AmountApprovedfrom the response. If less thanAmountDue(partial approval), the remaining balance = AmountDue − AmountApproved. - Payment 2 —
BeginPaymentSession→GetCardDetails(CardType: Credit) →Purchase(AmountDue: remaining balance) →EndPaymentSession. - Repeat for additional tenders until the check total is fully covered.
If the gift card has insufficient balance, the PMI Purchase response will return an AmountApproved less than AmountDue. Always compare these fields — do not assume full approval. Prompt for a second tender for the remaining balance.
Gift card as payment tender
Gift card payments use the standard GetCardDetails → Purchase flow, but with CardType: "GiftCard" specified in the card details. The response includes CardBalance (remaining balance on the card after the transaction).
# Step 2: GetCardDetails — specify GiftCard type (after BeginPaymentSession)
curl -X POST https://localhost:8600/PMI/GetCardDetails \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "GetCardDetails",
"RequestID": "1002", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "45.00", "AmountTendered": "45.00" },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-001" },
"CardDetails": [{ "CardType": "GiftCard" }]
}
}'
# Step 3: Purchase with gift card flag
curl -X POST https://localhost:8600/PMI/Purchase \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Purchase",
"RequestID": "1003", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "AmountDue": "45.00", "AmountTendered": "45.00" },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-001", "PaymentTranID": "pmt-gc-tran-001" },
"PosOptions": { "CardType": "GiftCard" }
}
}'
# ⚠️ Check response: AmountApproved may be < AmountDue (partial approval)
# Response.Amounts.CardBalance = remaining gift card balance// Step 2: GetCardDetails — GiftCard type (after BeginPaymentSession)
const gcdResp = await fetch('https://localhost:8600/PMI/GetCardDetails', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'GetCardDetails', RequestID: '1002', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '45.00', AmountTendered: '45.00' },
PaymentDetails: { SessionTranID: sessionTranID },
CardDetails: [{ CardType: 'GiftCard' }]
}})
});
const { Message: gcd } = await gcdResp.json();
const paymentTranID = gcd.PaymentDetails.PaymentTranID;
// Step 3: Purchase with GiftCard flag
const purResp = await fetch('https://localhost:8600/PMI/Purchase', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'Purchase', RequestID: '1003', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '45.00', AmountTendered: '45.00' },
PaymentDetails: { SessionTranID: sessionTranID, PaymentTranID: paymentTranID },
PosOptions: { CardType: 'GiftCard' }
}})
});
const { Message: pur } = await purResp.json();
// ⚠️ parseFloat(pur.Amounts.AmountApproved) < 45.00 → partial approval, prompt for another tender
const cardBalance = pur.Amounts.CardBalance;# Step 2: GetCardDetails — GiftCard type
gcd = requests.post(
'https://localhost:8600/PMI/GetCardDetails',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'GetCardDetails',
'RequestID': '1002', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '45.00', 'AmountTendered': '45.00'},
'PaymentDetails': {'SessionTranID': session_tran_id},
'CardDetails': [{'CardType': 'GiftCard'}]}},
verify=False
).json()['Message']
payment_tran_id = gcd['PaymentDetails']['PaymentTranID']
# Step 3: Purchase with GiftCard flag
pur = requests.post(
'https://localhost:8600/PMI/Purchase',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'Purchase',
'RequestID': '1003', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '45.00', 'AmountTendered': '45.00'},
'PaymentDetails': {'SessionTranID': session_tran_id, 'PaymentTranID': payment_tran_id},
'PosOptions': {'CardType': 'GiftCard'}}},
verify=False
).json()['Message']
# ⚠️ float(pur['Amounts']['AmountApproved']) < 45.00 → partial, prompt for second tender
card_balance = pur['Amounts']['CardBalance'] [
'ClientID' => 'POS1',
'Operation' => 'GetCardDetails',
'RequestID' => '1002',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '45.00',
'AmountTendered' => '45.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-001'
],
'CardDetails' => [[
'CardType' => 'GiftCard'
]]
]
]);
$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$paymentTranID = $msg['PaymentDetails']['PaymentTranID'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "GetCardDetails",
RequestID = "1002",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "45.00",
AmountTendered = "45.00"
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-001"
},
CardDetails = new[] {{'CardType': 'GiftCard'}}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var paymentTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("PaymentTranID").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'GetCardDetails',
RequestID: '1002',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '45.00',
AmountTendered: '45.00'
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-001'
},
CardDetails: [{'CardType': 'GiftCard'}]
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"1002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"45.00","AmountTendered":"45.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-001"},"CardDetails":[{"CardType":"GiftCard"}]}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.PaymentTranID package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "GetCardDetails",
"RequestID": "1002",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "45.00",
"AmountTendered": "45.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-001",
},
"CardDetails": []any{{'CardType': 'GiftCard'}},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["PaymentTranID"]}Gift card management
All gift card management operations follow the same session pattern: BeginPaymentSession → operation → EndPaymentSession. The card is presented on the PIN pad unless a PAN/Track is explicitly provided.
Activate
curl -X POST https://localhost:8600/PMI/Activate \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Activate",
"RequestID": "5001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "ActivationAmount": "50.00" },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-act" },
"PosOptions": { "CardType": "GiftCard" },
"PosTranID": "pos-gc-001"
}
}'
# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "ACT445566" },
# "Amounts": { "AmountApproved": "50.00", "CardBalance": "50.00" },
# "CardDetails": [{ "LastFour": "4400", "CardName": "GiftCard" }] } }const resp = await fetch('https://localhost:8600/PMI/Activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'Activate', RequestID: '5001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { ActivationAmount: '50.00' },
PaymentDetails: { SessionTranID: sessionTranID },
PosOptions: { CardType: 'GiftCard' },
PosTranID: 'pos-gc-001'
}})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → '50.00'
// Message.CardDetails[0].LastFour → '4400'result = requests.post(
'https://localhost:8600/PMI/Activate',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'Activate',
'RequestID': '5001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'ActivationAmount': '50.00'},
'PaymentDetails': {'SessionTranID': session_tran_id},
'PosOptions': {'CardType': 'GiftCard'},
'PosTranID': 'pos-gc-001'}},
verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance'] # '50.00' [
'ClientID' => 'POS1',
'Operation' => 'Activate',
'RequestID' => '5001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'ActivationAmount' => '50.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-act'
],
'PosOptions' => [
'CardType' => 'GiftCard'
],
'PosTranID' => 'pos-gc-001'
]
]);
$ch = curl_init('https://localhost:8600/PMI/Activate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$balance = $msg['Amounts']['CardBalance'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "Activate",
RequestID = "5001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
ActivationAmount = "50.00"
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-act"
},
PosOptions = new {
CardType = "GiftCard"
},
PosTranID = "pos-gc-001"
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Activate", payload);
var root = await resp.Content.ReadFromJsonAsync();
var balance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/Activate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'Activate',
RequestID: '5001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
ActivationAmount: '50.00'
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-act'
},
PosOptions: {
CardType: 'GiftCard'
},
PosTranID: 'pos-gc-001'
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
balance = msg['Amounts']['CardBalance']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Activate","RequestID":"5001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ActivationAmount":"50.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-act"},"PosOptions":{"CardType":"GiftCard"},"PosTranID":"pos-gc-001"}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/Activate"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "Activate",
"RequestID": "5001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"ActivationAmount": "50.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-act",
},
"PosOptions": map[string]any{
"CardType": "GiftCard",
},
"PosTranID": "pos-gc-001",
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Activate", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}BalanceInquiry
curl -X POST https://localhost:8600/PMI/BalanceInquiry \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "BalanceInquiry",
"RequestID": "5002", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-bal" }
}
}'
# Response
# { "Message": { "StatusCode": 0,
# "Amounts": { "CardBalance": "32.50" },
# "CardDetails": [{ "LastFour": "4400" }] } }const resp = await fetch('https://localhost:8600/PMI/BalanceInquiry', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'BalanceInquiry', RequestID: '5002', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
PaymentDetails: { SessionTranID: sessionTranID }
}})
});
const { Message } = await resp.json();
const cardBalance = Message.Amounts.CardBalance; // e.g. '32.50'result = requests.post(
'https://localhost:8600/PMI/BalanceInquiry',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'BalanceInquiry',
'RequestID': '5002', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'PaymentDetails': {'SessionTranID': session_tran_id}}},
verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance'] # e.g. '32.50' [
'ClientID' => 'POS1',
'Operation' => 'BalanceInquiry',
'RequestID' => '5002',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-bal'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/BalanceInquiry');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$balance = $msg['Amounts']['CardBalance'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "BalanceInquiry",
RequestID = "5002",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-bal"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/BalanceInquiry", payload);
var root = await resp.Content.ReadFromJsonAsync();
var balance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/BalanceInquiry')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'BalanceInquiry',
RequestID: '5002',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-bal'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
balance = msg['Amounts']['CardBalance']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"BalanceInquiry","RequestID":"5002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-gc-session-bal"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/BalanceInquiry"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "BalanceInquiry",
"RequestID": "5002",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-bal",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/BalanceInquiry", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}Reload
curl -X POST https://localhost:8600/PMI/Reload \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Reload",
"RequestID": "5003", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "ReloadAmount": "25.00" },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-reload" }
}
}'
# Response — new balance returned
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "RLD778899" },
# "Amounts": { "AmountApproved": "25.00", "CardBalance": "57.50" } } }const resp = await fetch('https://localhost:8600/PMI/Reload', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'Reload', RequestID: '5003', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { ReloadAmount: '25.00' },
PaymentDetails: { SessionTranID: sessionTranID }
}})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → new balance (e.g. '57.50')result = requests.post(
'https://localhost:8600/PMI/Reload',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'Reload',
'RequestID': '5003', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'ReloadAmount': '25.00'},
'PaymentDetails': {'SessionTranID': session_tran_id}}},
verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance'] # new balance e.g. '57.50' [
'ClientID' => 'POS1',
'Operation' => 'Reload',
'RequestID' => '5003',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'ReloadAmount' => '25.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-reload'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/Reload');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$newBalance = $msg['Amounts']['CardBalance'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "Reload",
RequestID = "5003",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
ReloadAmount = "25.00"
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-reload"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Reload", payload);
var root = await resp.Content.ReadFromJsonAsync();
var newBalance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/Reload')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'Reload',
RequestID: '5003',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
ReloadAmount: '25.00'
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-reload'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
new_balance = msg['Amounts']['CardBalance']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Reload","RequestID":"5003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ReloadAmount":"25.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-reload"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/Reload"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance (new balance after reload) package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "Reload",
"RequestID": "5003",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"ReloadAmount": "25.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-reload",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Reload", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}Deactivate
curl -X POST https://localhost:8600/PMI/Deactivate \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "Deactivate",
"RequestID": "5004", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": { "ActivationAmount": "50.00" },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-deact" }
}
}'
# Response
# { "Message": { "StatusCode": 0, "Amounts": { "CardBalance": "0.00" } } }const resp = await fetch('https://localhost:8600/PMI/Deactivate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'Deactivate', RequestID: '5004', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { ActivationAmount: '50.00' },
PaymentDetails: { SessionTranID: sessionTranID }
}})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → '0.00'result = requests.post(
'https://localhost:8600/PMI/Deactivate',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'Deactivate',
'RequestID': '5004', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'ActivationAmount': '50.00'},
'PaymentDetails': {'SessionTranID': session_tran_id}}},
verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance'] # '0.00' [
'ClientID' => 'POS1',
'Operation' => 'Deactivate',
'RequestID' => '5004',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'ActivationAmount' => '50.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-deact'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/Deactivate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Amounts']['CardBalance'] === '0.00'using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "Deactivate",
RequestID = "5004",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
ActivationAmount = "50.00"
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-deact"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Deactivate", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/Deactivate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'Deactivate',
RequestID: '5004',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
ActivationAmount: '50.00'
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-deact'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
# msg['Amounts']['CardBalance'] == '0.00'import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Deactivate","RequestID":"5004","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ActivationAmount":"50.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-deact"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/Deactivate"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance === "0.00" package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "Deactivate",
"RequestID": "5004",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"ActivationAmount": "50.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-deact",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Deactivate", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}CashOut
curl -X POST https://localhost:8600/PMI/CashOut \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "CashOut",
"RequestID": "5005", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"PaymentDetails": { "SessionTranID": "ccl-gc-session-cashout" },
"PosOptions": { "TranType": "CashOut" }
}
}'
# Response — balance dispensed as cash
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "CSH334455" },
# "Amounts": { "AmountApproved": "57.50", "CardBalance": "0.00" } } }const resp = await fetch('https://localhost:8600/PMI/CashOut', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'CashOut', RequestID: '5005', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
PaymentDetails: { SessionTranID: sessionTranID },
PosOptions: { TranType: 'CashOut' }
}})
});
const { Message } = await resp.json();
// Message.Amounts.AmountApproved → amount cashed out
// Message.Amounts.CardBalance → '0.00'result = requests.post(
'https://localhost:8600/PMI/CashOut',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'CashOut',
'RequestID': '5005', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'PaymentDetails': {'SessionTranID': session_tran_id},
'PosOptions': {'TranType': 'CashOut'}}},
verify=False
).json()['Message']
amount_cashed_out = result['Amounts']['AmountApproved']
card_balance = result['Amounts']['CardBalance'] # '0.00' [
'ClientID' => 'POS1',
'Operation' => 'CashOut',
'RequestID' => '5005',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-gc-session-cashout'
],
'PosOptions' => [
'TranType' => 'CashOut'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/CashOut');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$dispensed = $msg['Amounts']['AmountApproved'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "CashOut",
RequestID = "5005",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
PaymentDetails = new {
SessionTranID = "ccl-gc-session-cashout"
},
PosOptions = new {
TranType = "CashOut"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/CashOut", payload);
var root = await resp.Content.ReadFromJsonAsync();
var dispensed = root.GetProperty("Message").GetProperty("Amounts").GetProperty("AmountApproved").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/CashOut')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'CashOut',
RequestID: '5005',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
PaymentDetails: {
SessionTranID: 'ccl-gc-session-cashout'
},
PosOptions: {
TranType: 'CashOut'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
dispensed = msg['Amounts']['AmountApproved']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"CashOut","RequestID":"5005","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-gc-session-cashout"},"PosOptions":{"TranType":"CashOut"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/CashOut"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.AmountApproved (cash dispensed), CardBalance → "0.00" package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "CashOut",
"RequestID": "5005",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-gc-session-cashout",
},
"PosOptions": map[string]any{
"TranType": "CashOut",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/CashOut", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Amounts"].(map[string]any)["AmountApproved"]}IncrementalAuth
For bar tabs with high expected spending, you can add incremental authorizations before closing. Each IncrementalAuth extends the secured amount. Finalize with IncrementalCompletion.
curl -X POST https://localhost:8600/PMI/IncrementalAuth \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "IncrementalAuth",
"RequestID": "7001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
"Amounts": {
"AmountDue": "50.00",
"AmountTendered": "50.00",
"TotalAmountTendered": "150.00"
},
"PaymentDetails": { "SessionTranID": "ccl-inc-session-001" },
"CardDetails": [{ "CardType": "Credit" }],
"AdjustmentData": { "OriginalReferenceId": "tab-ref-xyz789" }
}
}'
# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "INC223344" },
# "HostData": { "ReferenceID": "inc-ref-new-001" },
# "Amounts": { "AmountApproved": "50.00", "TotalAmountTendered": "150.00" } } }const resp = await fetch('https://localhost:8600/PMI/IncrementalAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'IncrementalAuth', RequestID: '7001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
Amounts: { AmountDue: '50.00', AmountTendered: '50.00', TotalAmountTendered: '150.00' },
PaymentDetails: { SessionTranID: sessionTranID },
CardDetails: [{ CardType: 'Credit' }],
AdjustmentData: { OriginalReferenceId: originalReferenceId }
}})
});
const { Message } = await resp.json();
const newReferenceId = Message.HostData.ReferenceID;result = requests.post(
'https://localhost:8600/PMI/IncrementalAuth',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'IncrementalAuth',
'RequestID': '7001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
'Amounts': {'AmountDue': '50.00', 'AmountTendered': '50.00', 'TotalAmountTendered': '150.00'},
'PaymentDetails': {'SessionTranID': session_tran_id},
'CardDetails': [{'CardType': 'Credit'}],
'AdjustmentData': {'OriginalReferenceId': original_reference_id}}},
verify=False
).json()['Message']
new_reference_id = result['HostData']['ReferenceID'] [
'ClientID' => 'POS1',
'Operation' => 'IncrementalAuth',
'RequestID' => '7001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
],
'Amounts' => [
'AmountDue' => '50.00',
'AmountTendered' => '50.00',
'TotalAmountTendered' => '150.00'
],
'PaymentDetails' => [
'SessionTranID' => 'ccl-inc-session-001'
],
'CardDetails' => [[
'CardType' => 'Credit'
]],
'AdjustmentData' => [
'OriginalReferenceId' => 'tab-ref-xyz789'
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/IncrementalAuth');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$newRef = $msg['HostData']['ReferenceID'];using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "IncrementalAuth",
RequestID = "7001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
},
Amounts = new {
AmountDue = "50.00",
AmountTendered = "50.00",
TotalAmountTendered = "150.00"
},
PaymentDetails = new {
SessionTranID = "ccl-inc-session-001"
},
CardDetails = new[] {{'CardType': 'Credit'}},
AdjustmentData = new {
OriginalReferenceId = "tab-ref-xyz789"
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/IncrementalAuth", payload);
var root = await resp.Content.ReadFromJsonAsync();
var newRef = root.GetProperty("Message").GetProperty("HostData").GetProperty("ReferenceID").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/IncrementalAuth')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'IncrementalAuth',
RequestID: '7001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
},
Amounts: {
AmountDue: '50.00',
AmountTendered: '50.00',
TotalAmountTendered: '150.00'
},
PaymentDetails: {
SessionTranID: 'ccl-inc-session-001'
},
CardDetails: [{'CardType': 'Credit'}],
AdjustmentData: {
OriginalReferenceId: 'tab-ref-xyz789'
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
new_ref = msg['HostData']['ReferenceID']import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"IncrementalAuth","RequestID":"7001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"50.00","AmountTendered":"50.00","TotalAmountTendered":"150.00"},"PaymentDetails":{"SessionTranID":"ccl-inc-session-001"},"CardDetails":[{"CardType":"Credit"}],"AdjustmentData":{"OriginalReferenceId":"tab-ref-xyz789"}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/IncrementalAuth"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.HostData.ReferenceID (new reference for further incremental auths) package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "IncrementalAuth",
"RequestID": "7001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
"Amounts": map[string]any{
"AmountDue": "50.00",
"AmountTendered": "50.00",
"TotalAmountTendered": "150.00",
},
"PaymentDetails": map[string]any{
"SessionTranID": "ccl-inc-session-001",
},
"CardDetails": []any{{'CardType': 'Credit'}},
"AdjustmentData": map[string]any{
"OriginalReferenceId": "tab-ref-xyz789",
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/IncrementalAuth", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["HostData"].(map[string]any)["ReferenceID"]}Device notifications
During GetCardDetails, the PIN pad displays prompts to the customer ("Tap Card", "Insert Chip", etc.). Your POS application should poll GetNotifications in a background loop to receive these messages and display them to the server/cashier.
StartNotifications
# Call before GetCardDetails to activate the PIN pad display
curl -X POST https://localhost:8600/PMI/StartNotifications \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "StartNotifications",
"RequestID": "9001", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
}
}'
# Response
# { "Message": { "StatusCode": 0, "StatusMessage": "Notifications started" } }// Call before GetCardDetails to activate the PIN pad display
const resp = await fetch('https://localhost:8600/PMI/StartNotifications', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'StartNotifications', RequestID: '9001', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
}})
});
const { Message } = await resp.json();
// Message.StatusMessage → 'Notifications started'# Call before GetCardDetails to activate the PIN pad display
result = requests.post(
'https://localhost:8600/PMI/StartNotifications',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'StartNotifications',
'RequestID': '9001', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}}},
verify=False
).json()['Message']
# result['StatusMessage'] → 'Notifications started' [
'ClientID' => 'POS1',
'Operation' => 'StartNotifications',
'RequestID' => '9001',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/StartNotifications');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "StartNotifications",
RequestID = "9001",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/StartNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/StartNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'StartNotifications',
RequestID: '9001',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"StartNotifications","RequestID":"9001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/StartNotifications"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success" package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "StartNotifications",
"RequestID": "9001",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/StartNotifications", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Status"]}GetNotifications
Poll this endpoint repeatedly while waiting for the card. It returns PIN pad display messages (e.g., "Tap Card", "Please Wait") as they occur. The timeout query parameter (seconds) holds the connection open until a notification is available — an empty body means nothing is queued yet, which is normal.
curl -X POST "https://localhost:8600/PMI/GetNotifications?timeout=3" \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "GetNotifications",
"RequestID": "9002", "Version": "4.7.0"
}
}'
# Response — notification available
# { "Notification": [
# { "CustomerText": "Please tap, insert, or swipe your card" },
# { "CustomerText": "Processing..." }
# ] }
# Response — nothing queued (empty body or empty array — normal, keep polling)// Poll in a loop while waiting for GetCardDetails to return
async function pollNotifications(sessionActive) {
while (sessionActive()) {
const resp = await fetch('https://localhost:8600/PMI/GetNotifications?timeout=3', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'GetNotifications', RequestID: '9002', Version: '4.7.0'
}})
});
const data = await resp.json().catch(() => null);
if (data?.Notification?.length) {
data.Notification.forEach(n => displayMessage(n.CustomerText));
}
}
}# Poll in a loop while waiting for GetCardDetails to return
import threading
def poll_notifications(stop_event):
while not stop_event.is_set():
try:
resp = requests.post(
'https://localhost:8600/PMI/GetNotifications',
params={'timeout': 3},
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'GetNotifications',
'RequestID': '9002', 'Version': '4.7.0'}},
verify=False, timeout=5
).json()
for n in resp.get('Notification', []):
print(n.get('CustomerText', ''))
except Exception:
pass [
'ClientID' => 'POS1',
'Operation' => 'GetNotifications',
'RequestID' => '9002',
'Version' => '4.7.0'
]
]);
$ch = curl_init('https://localhost:8600/PMI/GetNotifications');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $data['Notification'] — array of customer-facing stringsusing System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "GetNotifications",
RequestID = "9002",
Version = "4.7.0"
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Notification").EnumerateArray() require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/GetNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'GetNotifications',
RequestID: '9002',
Version: '4.7.0'
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
# data['Notification'] # array of customer-facing stringsimport java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetNotifications","RequestID":"9002","Version":"4.7.0"}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/GetNotifications"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Parse resp.body() → Notification[] array package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "GetNotifications",
"RequestID": "9002",
"Version": "4.7.0",
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetNotifications", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Notification"].([]any)}StopNotifications
# Call after card is captured (GetCardDetails returns)
curl -X POST https://localhost:8600/PMI/StopNotifications \
-H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
-d '{
"Message": {
"ClientID": "POS1", "Operation": "StopNotifications",
"RequestID": "9003", "Version": "4.7.0",
"ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
}
}'
# Response
# { "Message": { "StatusCode": 0, "StatusMessage": "Notifications stopped" } }// Call after card is captured (GetCardDetails returns)
const resp = await fetch('https://localhost:8600/PMI/StopNotifications', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
body: JSON.stringify({ Message: {
ClientID: 'POS1', Operation: 'StopNotifications', RequestID: '9003', Version: '4.7.0',
ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
}})
});
const { Message } = await resp.json();
// Message.StatusMessage → 'Notifications stopped'# Call after card is captured (GetCardDetails returns)
result = requests.post(
'https://localhost:8600/PMI/StopNotifications',
headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
json={'Message': {'ClientID': 'POS1', 'Operation': 'StopNotifications',
'RequestID': '9003', 'Version': '4.7.0',
'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}}},
verify=False
).json()['Message']
# result['StatusMessage'] → 'Notifications stopped' [
'ClientID' => 'POS1',
'Operation' => 'StopNotifications',
'RequestID' => '9003',
'Version' => '4.7.0',
'ConfigOptions' => [
'CompanyNumber' => 185197,
'StoreNumber' => 1,
'LaneNumber' => 1
]
]
]);
$ch = curl_init('https://localhost:8600/PMI/StopNotifications');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'using System.Net.Http.Json;
var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");
var payload = new {
Message = new {
ClientID = "POS1",
Operation = "StopNotifications",
RequestID = "9003",
Version = "4.7.0",
ConfigOptions = new {
CompanyNumber = 185197,
StoreNumber = 1,
LaneNumber = 1
}
}
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/StopNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString(); require 'net/http'
require 'json'
require 'openssl'
uri = URI('https://localhost:8600/PMI/StopNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # dev only
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
Message: {
ClientID: 'POS1',
Operation: 'StopNotifications',
RequestID: '9003',
Version: '4.7.0',
ConfigOptions: {
CompanyNumber: 185197,
StoreNumber: 1,
LaneNumber: 1
}
}
}.to_json
msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.sslContext(buildTrustAllContext()) // see pmi-integration guide
.build();
String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"StopNotifications","RequestID":"9003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8600/PMI/StopNotifications"))
.header("Content-Type", "application/json")
.header("X-ClientId", "POS1")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success" package main
import (
"bytes"; "crypto/tls"; "encoding/json"; "net/http"
)
func main() {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
body, _ := json.Marshal(map[string]any{
"Message": map[string]any{
"ClientID": "POS1",
"Operation": "StopNotifications",
"RequestID": "9003",
"Version": "4.7.0",
"ConfigOptions": map[string]any{
"CompanyNumber": 185197,
"StoreNumber": 1,
"LaneNumber": 1,
},
},
})
req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/StopNotifications", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-ClientId", "POS1")
resp, _ := client.Do(req)
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
// result["Message"].(map[string]any)["Status"]}API quick reference
| Operation | Endpoint | Restaurant use | Session required |
|---|---|---|---|
BeginPaymentSession |
POST /BeginPaymentSession |
Opens every payment flow | Creates session |
EndPaymentSession |
POST /EndPaymentSession |
Closes every payment flow | Closes session |
GetCardDetails |
POST /GetCardDetails |
Card capture (credit, debit, gift); add PromptTip: "YES" for device tip |
✓ Required |
Purchase |
POST /Purchase |
Standard payment & gift card tender | ✓ Required |
OpenTab |
POST /OpenTab |
Store card for bar tab (no charge) | ✓ Required |
CloseTab |
POST /CloseTab |
Charge stored bar tab card (with tip) | ✓ New session |
Adjustment |
POST /Adjustment |
Add tip after authorization | ✓ New session |
IncrementalAuth |
POST /IncrementalAuth |
Extend bar tab authorization limit | ✓ Required |
IncrementalCompletion |
POST /IncrementalCompletion |
Finalize incremental auth chain | ✓ Required |
Activate |
POST /Activate |
Activate new gift card | ✓ Required |
BalanceInquiry |
POST /BalanceInquiry |
Check gift card balance | ✓ Required |
Reload |
POST /Reload |
Add funds to gift card | ✓ Required |
Deactivate |
POST /Deactivate |
Cancel/deactivate gift card | ✓ Required |
CashOut |
POST /CashOut |
Cash out remaining gift card balance | ✓ Required |
StartNotifications |
POST /StartNotifications |
Activate PIN pad display & message queue | Optional |
GetNotifications |
POST /GetNotifications?timeout=3 |
Poll PIN pad prompts during card wait | Optional |
StopNotifications |
POST /StopNotifications |
Stop notification queue after card captured | Optional |
Status codes
| StatusCode | Meaning | Action |
|---|---|---|
0 | Success / Approved | Proceed |
1 | General error | Show error, EndPaymentSession |
2 | Declined | Prompt guest for alternate tender |
3 | Partial approval | Collect remaining balance with another tender |
4 | Card removed during read | Ask guest to re-present card |
5 | Timeout | Cancel and EndPaymentSession |
6 | Communication error | Retry or EndPaymentSession |
7 | Session not found | EndPaymentSession, start new session |
8 | Duplicate request | Check if previous request succeeded |
9 | Engine not initialized | Call Initialize → OpenLane first |
10 | Lane not open | Call OpenLane |
Best practices
Persisting transaction data
For every approved Purchase, OpenTab, or CloseTab, persist these fields to your database immediately — before EndPaymentSession:
HostData.ReferenceID— required forAdjustment,IncrementalAuth,IncrementalCompletionPaymentDetails.PaymentTranID— required forAdjustmentandCloseTabPaymentDetails.AuthCode— for receipts and audit trailCardDetails.CardBrand+LastFour— for receipts and chargebacks
Error handling
Check StatusCode in every PMI response. Non-zero codes require action before proceeding:
| StatusCode | Meaning | Required action |
|---|---|---|
2 — Declined | Issuer declined the card | Call EndPaymentSession, then prompt for alternate tender |
3 — Partial | AmountApproved < AmountDue | Call EndPaymentSession, collect remaining balance with second tender |
4 — Card removed | Card pulled out mid-read | Call EndPaymentSession, ask guest to re-present card |
5 — Timeout | No card presented in time | Call EndPaymentSession, retry or cancel |
6 — Comm error | Network/gateway unreachable | Retry once; if still failing, call EndPaymentSession |
| Any non-zero | See status codes table | Always call EndPaymentSession before opening a new session |
Receipt content for restaurants
A compliant restaurant receipt must include:
- Table number, server name, check number, date & time
- Itemized list with seat assignment (for split checks)
- Subtotal, tax, tip (if included), total
- Payment method: card brand + last four digits
- PAYMENT AUTH: authorization code from
PurchaseorCloseTab - TIP ADJ AUTH: adjustment auth code if tip was added post-authorization
- For gift cards:
CardBalanceremaining after transaction