Merchant Onboarding Service
1. Overview
The Merchant Onboarding Service is a dedicated microservice inside the CDE project (peakpos-cde). Its sole purpose is to bridge the gap between the gateway's TransIT credentials and physical payment devices that need those credentials. It handles TransIT MerchantActivation API calls and pushes resulting credentials to NexGO's XTMS device management system.
Why this service exists: TransIT credentials (deviceID, transactionKey) must never leave the CDE boundary. External systems (e.g., PeakPOS Management API) can only trigger provisioning workflows — they never see, receive, or transmit raw credentials.
Rename note: this page still shows legacy
merchantIdfields where the shipped provisioning and credential APIs use them today. Those values should be read as concrete location ids on the wire.
1.1 Rename guardrails
Safe statements:
- provisioning requests still use legacy
merchantIdfield names on the wire - those identifiers point at the concrete location/device ownership boundary
- the surrounding tenant model remains organization/location-first
Unsafe statements:
- that provisioning still depends on a distinct merchant-first tenant model
- that credential or job APIs have fully renamed away from
merchantId - that support should describe onboarding as an exception to the rename
2. Responsibilities
| Responsibility | Description |
|---|---|
| Merchant activation | Call TransIT MerchantActivation API to generate transactionKey + activate deviceID |
| Device credential push | Push TransIT credentials to NexGO XTMS by device serial number |
| Credential storage | Encrypt and store TransIT credentials in CDE Spanner via Cloud KMS |
| Credential rotation | Rotate transactionKeys on demand or on schedule |
| Provisioning audit trail | Immutable log of every provisioning action (who, what, when, outcome) |
| Job orchestration | Manage async provisioning jobs with retry, dead-letter, and manual replay |
| Status reporting | Return provisioning job status to callers (without exposing credentials) |
3. Architecture Context
┌──────────────────────────────────────────────┐
│ GCP Project: peakpos (OUT of CDE) │
│ │
│ Management API │
│ │ │
│ │ POST /api/v1/provisioning/jobs │
│ │ OAuth token (scope: provision:request) │
│ │ Body: { merchantId, deviceSerial } │
│ │ Response: { jobId, status } │
└────┼─────────────────────────────────────────┘
│
│ Cloud Run IAM-gated HTTPS
│
┌────┼──────────────────────────────────────────────────────────────┐
│ ▼ │
│ Merchant Onboarding Service (CDE project) │
│ │ │
│ ├──→ TransIT MerchantActivation API (get credentials) │
│ ├──→ CDE Spanner (store encrypted credentials) │
│ ├──→ Cloud KMS (encrypt/decrypt credential material) │
│ └──→ NexGO XTMS API (push credentials to device by serial) │
│ │
│ GCP Project: peakpos-cde (PCI SCOPED) │
└───────────────────────────────────────────────────────────────────┘
4. API Specification
Authentication
All endpoints require a Gateway OAuth token issued by the Gateway Auth Service:
Authorization: Bearer <gateway-oauth-token>
Tokens are scoped — callers can only access endpoints matching their granted scopes.
Operationally, this means the legacy field names in the request body or status response are compatibility details, not a separate authorization or tenancy model.
4.1 External API (called by clients like PeakPOS Management API)
Create Provisioning Job
POST /api/v1/provisioning/jobs
Required Scope: provision:request
Request:
{
"merchantId": "loc_abc123",
"deviceSerial": "N5-2024-001234",
"action": "ACTIVATE", // ACTIVATE | DEACTIVATE | ROTATE_KEY
"callbackUrl": "https://mgmt-api.peakgateway.com/webhooks/provisioning", // optional
"requestedBy": "user_xyz789", // identity of the human who initiated this
"metadata": {
"storeId": "store_456",
"terminalId": "term_789",
"notes": "New c-store install at 123 Main St"
}
}
Response (202 Accepted):
{
"jobId": "prov_job_abc123",
"status": "QUEUED",
"merchantId": "loc_abc123",
"deviceSerial": "N5-2024-001234",
"action": "ACTIVATE",
"createdAt": "2026-03-08T15:00:00Z",
"estimatedCompletionSeconds": 30
}
Note: 202 (not 200) — provisioning is async. The response contains a job ID for polling or the caller can register a callback URL.
Get Job Status
GET /api/v1/provisioning/jobs/{jobId}
Required Scope: provision:request
Response (200):
{
"jobId": "prov_job_abc123",
"status": "COMPLETED", // QUEUED | IN_PROGRESS | COMPLETED | FAILED | RETRYING
"merchantId": "loc_abc123",
"deviceSerial": "N5-2024-001234",
"action": "ACTIVATE",
"steps": [
{
"step": "TRANSIT_ACTIVATION",
"status": "COMPLETED",
"completedAt": "2026-03-08T15:00:05Z"
},
{
"step": "CREDENTIAL_STORAGE",
"status": "COMPLETED",
"completedAt": "2026-03-08T15:00:06Z"
},
{
"step": "XTMS_PUSH",
"status": "COMPLETED",
"completedAt": "2026-03-08T15:00:12Z"
}
],
"createdAt": "2026-03-08T15:00:00Z",
"completedAt": "2026-03-08T15:00:12Z",
"error": null
}
Note: Steps are visible for transparency. Credential values are NEVER included in any response.
List Jobs
GET /api/v1/provisioning/jobs?merchantId={merchantId}&status={status}&page={page}&size={size}
Required Scope: provision:request
Response (200):
{
"jobs": [ ... ],
"page": 1,
"size": 20,
"totalElements": 3
}
Cancel Job
POST /api/v1/provisioning/jobs/{jobId}/cancel
Required Scope: provision:request
Response (200):
{
"jobId": "prov_job_abc123",
"status": "CANCELLED",
"cancelledAt": "2026-03-08T15:00:03Z",
"cancelledBy": "user_xyz789"
}
Only cancellable if status is
QUEUED. In-progress jobs cannot be cancelled.
Retry Failed Job
POST /api/v1/provisioning/jobs/{jobId}/retry
Required Scope: provision:request
Response (202 Accepted):
{
"jobId": "prov_job_abc123",
"status": "QUEUED",
"retryCount": 2,
"retriedAt": "2026-03-08T16:00:00Z"
}
4.2 Internal API (called by other CDE services only)
Get Merchant Credentials (Processing Service only) — legacy
Deprecated, still wired.
GET /internal/merchants/{merchantId}/credentialsremains callable server-side for backwards compatibility, but it is no longer surfaced in the OpenAPI sidebar and new integrations should read from thecredential_profiles-backed path instead (seelibs/schema/.../CredentialProfile.sq). The legacy route will be retired alongside the CredentialService rewrite tracked in ORG-14 A1b; the shape below is kept for reference only. VPC-internal only, never exposed through the API Gateway, Processing Service service account only, fully audit-logged.
GET /internal/merchants/{merchantId}/credentials (legacy)
Required: Service-to-service IAM (not OAuth — internal VPC only)
Caller: Processing Service ONLY
Response (200):
{
"merchantId": "loc_abc123",
"deviceId": "88700000123456",
"transactionKey": "****REDACTED_IN_LOGS****",
"activatedAt": "2026-03-08T15:00:05Z",
"lastRotatedAt": null
}
Rotate Credentials
POST /internal/merchants/{merchantId}/rotate
Required: Service-to-service IAM
Caller: Processing Service or scheduled job
Response (200):
{
"merchantId": "loc_abc123",
"rotatedAt": "2026-03-08T15:00:00Z",
"previousKeyDeactivated": true,
"xtmsPushStatus": "COMPLETED"
}
5. Provisioning Job State Machine
┌─────────┐
┌──────────>│ QUEUED │
│ └────┬────┘
│ │
(retry) │ worker picks up
│ ▼
│ ┌──────────────┐
├───────────│ IN_PROGRESS │
│ └──┬───────┬───┘
│ │ │
│ success failure
│ │ │
│ ▼ ▼
│ ┌───────────┐ ┌────────────┐
│ │ COMPLETED │ │ RETRYING │──── max retries exceeded ──→ FAILED
│ └───────────┘ └─────┬──────┘
│ │
└─────────────────────────┘
CANCELLED ← (only from QUEUED, via cancel endpoint)
Job Steps (executed sequentially)
| Step | Action | Rollback on failure |
|---|---|---|
TRANSIT_ACTIVATION | Call TransIT MerchantActivation API | None (idempotent — can retry safely) |
CREDENTIAL_STORAGE | Encrypt credentials via KMS, store in CDE Spanner | Delete stored record |
XTMS_PUSH | Push credentials to NexGO XTMS by device serial | Mark as pending manual intervention |
Retry Policy
| Failure type | Retry strategy |
|---|---|
| TransIT API timeout/5xx | Exponential backoff: 5s, 15s, 45s (max 3 retries) |
| TransIT API 4xx | No retry (invalid request — fail immediately) |
| XTMS API timeout/5xx | Exponential backoff: 10s, 30s, 90s (max 3 retries) |
| XTMS API 4xx | No retry — fail and alert |
| KMS/Spanner errors | Exponential backoff: 2s, 4s, 8s (max 3 retries) |
| Max retries exceeded | Move to dead-letter queue, alert on-call, status = FAILED |
6. IAM & Service Account Matrix
Service Accounts
| Service Account | GCP Project | Purpose |
|---|---|---|
merchant-onboarding-sa@peakpos-cde | peakpos-cde | Runs the Merchant Onboarding Service |
processing-sa@peakpos-cde | peakpos-cde | Processing Service — calls internal credential endpoint |
gateway-auth-sa@peakpos-cde | peakpos-cde | Gateway Auth Service — validates OAuth tokens |
mgmt-api-sa@peakpos | peakpos | Management API — calls external provisioning API |
IAM Role Bindings
| Principal | Role | Resource | Purpose |
|---|---|---|---|
merchant-onboarding-sa | roles/secretmanager.secretAccessor | TransIT developer credentials secret | Read TransIT developerID/developerKey |
merchant-onboarding-sa | roles/cloudkms.cryptoKeyEncrypterDecrypter | Credential encryption key | Encrypt/decrypt merchant transactionKeys |
merchant-onboarding-sa | roles/spanner.databaseUser | CDE Spanner instance | Read/write provisioning tables |
merchant-onboarding-sa | roles/pubsub.publisher | provisioning-events topic | Publish provisioning audit events |
processing-sa | roles/run.invoker | Merchant Onboarding Service (internal) | Call internal credential endpoint |
mgmt-api-sa@peakpos | roles/run.invoker | Merchant Onboarding Service (external) | Call external provisioning API |
mgmt-api-sa@peakpos | (none on secrets/KMS/Spanner) | — | Cannot access credentials, keys, or CDE data |
OAuth Scope Matrix
| Scope | Who gets it | What it allows |
|---|---|---|
provision:request | PeakPOS Management API, Gateway Portal users | Create/view/cancel/retry provisioning jobs |
provision:admin | Gateway Portal admins only | View audit logs, manual replay, configuration |
merchant:activate | PeakPOS Management API, Gateway Portal users | Trigger merchant activation in TransIT |
credentials:read | Processing Service only (via IAM, not OAuth) | Read raw TransIT credentials |
credentials:rotate | Processing Service, scheduled jobs (via IAM) | Rotate TransIT credentials |
7. Data Model (CDE Spanner)
provisioning_jobs
CREATE TABLE provisioning_jobs (
job_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id VARCHAR(50) NOT NULL,
device_serial VARCHAR(100) NOT NULL,
action VARCHAR(20) NOT NULL, -- ACTIVATE, DEACTIVATE, ROTATE_KEY
status VARCHAR(20) NOT NULL, -- QUEUED, IN_PROGRESS, COMPLETED, FAILED, RETRYING, CANCELLED
requested_by VARCHAR(100) NOT NULL, -- OAuth subject (user ID)
client_id VARCHAR(100) NOT NULL, -- OAuth client (e.g., "peakpos-mgmt-api")
callback_url VARCHAR(500),
metadata JSONB DEFAULT '{}',
current_step VARCHAR(30), -- Current step being executed
retry_count INTEGER DEFAULT 0,
error_message TEXT,
error_code VARCHAR(20),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
cancelled_at TIMESTAMPTZ
);
CREATE INDEX idx_prov_jobs_merchant ON provisioning_jobs (merchant_id);
CREATE INDEX idx_prov_jobs_status ON provisioning_jobs (status);
CREATE INDEX idx_prov_jobs_client ON provisioning_jobs (client_id);
merchant_credentials (encrypted) — legacy
Legacy per-merchant credential table. The authoritative credential surface is now
credential_profiles(seelibs/schema/.../CredentialProfile.sq), which scopes credentials to an organization and is assignable to merchants and locations.merchant_credentialsis retained server-side for backwards compatibility and will be migrated ontocredential_profilesin ORG-14 A1b.
CREATE TABLE merchant_credentials (
credential_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id VARCHAR(50) NOT NULL UNIQUE,
transit_device_id BYTEA NOT NULL, -- KMS-encrypted
transaction_key BYTEA NOT NULL, -- KMS-encrypted
transit_merchant_id VARCHAR(50) NOT NULL, -- Not sensitive (returned in SmartConnect responses)
key_version INTEGER NOT NULL DEFAULT 1,
kms_key_name VARCHAR(200) NOT NULL, -- Full KMS key resource path
status VARCHAR(20) NOT NULL, -- ACTIVE, ROTATING, DEACTIVATED
activated_at TIMESTAMPTZ NOT NULL,
last_rotated_at TIMESTAMPTZ,
deactivated_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
provisioning_audit_log (append-only)
CREATE TABLE provisioning_audit_log (
log_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
job_id UUID NOT NULL,
event_type VARCHAR(50) NOT NULL, -- See audit events below
actor VARCHAR(100) NOT NULL, -- User ID or service account
actor_type VARCHAR(20) NOT NULL, -- USER, SERVICE, SYSTEM
client_id VARCHAR(100), -- OAuth client ID
merchant_id VARCHAR(50) NOT NULL,
device_serial VARCHAR(100),
details JSONB DEFAULT '{}', -- Step-specific details (NEVER contains credentials)
ip_address VARCHAR(45),
timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_prov_audit_merchant ON provisioning_audit_log (merchant_id);
CREATE INDEX idx_prov_audit_job ON provisioning_audit_log (job_id);
CREATE INDEX idx_prov_audit_time ON provisioning_audit_log (timestamp);
8. Audit Events
Every action produces an immutable audit log entry. Credential values are NEVER logged.
| Event Type | Trigger | Details captured |
|---|---|---|
PROVISION_REQUESTED | Job created via external API | merchantId, deviceSerial, action, requestedBy, clientId, IP |
PROVISION_STARTED | Worker picks up job | jobId, step |
TRANSIT_ACTIVATION_SUCCESS | TransIT MerchantActivation returns 200 | merchantId, transitMerchantId (not keys), responseCode |
TRANSIT_ACTIVATION_FAILED | TransIT returns error | merchantId, errorCode, errorMessage, retryCount |
CREDENTIAL_STORED | Encrypted credential written to Spanner | merchantId, keyVersion, kmsKeyName |
CREDENTIAL_ROTATED | Key rotation completed | merchantId, previousKeyVersion, newKeyVersion |
CREDENTIAL_ACCESSED | Processing Service reads credentials | merchantId, accessedBy (service account), purpose |
XTMS_PUSH_SUCCESS | XTMS accepts credential push | merchantId, deviceSerial, xtmsResponseCode |
XTMS_PUSH_FAILED | XTMS rejects or times out | merchantId, deviceSerial, errorCode, retryCount |
PROVISION_COMPLETED | All steps finished successfully | jobId, totalDurationMs |
PROVISION_FAILED | Max retries exceeded | jobId, failedStep, errorCode, errorMessage |
PROVISION_CANCELLED | Job cancelled by user | jobId, cancelledBy, IP |
PROVISION_RETRIED | Manual retry triggered | jobId, retriedBy, retryCount |
CREDENTIAL_DEACTIVATED | Merchant deactivated | merchantId, deactivatedBy, reason |
9. Security Controls
Credential Handling
- Encryption at rest: All TransIT credentials encrypted via Cloud KMS (AES-256-GCM) before Spanner write
- Encryption in transit: TLS 1.2+ on all connections (internal and external)
- Ephemeral in memory: Credentials decrypted only for the duration of the XTMS push call, then zeroed
- No logging of secrets: Credential values excluded from all application logs, audit logs, and error messages
- No credential return: External API never returns credential values — only job status
Network Isolation
- Service runs on Cloud Run in the CDE VPC
- External API reachable only through Gateway API Gateway (Cloud Endpoints) with OAuth validation
- Internal API reachable only via VPC-internal Cloud Run URL (no public endpoint)
- Egress restricted to: TransIT API, XTMS API, CDE Spanner, Cloud KMS, Pub/Sub
Rate Limiting
| Endpoint | Limit | Per |
|---|---|---|
POST /api/v1/provisioning/jobs | 10/min | client_id |
GET /api/v1/provisioning/jobs/{jobId} | 60/min | client_id |
GET /internal/merchants/{merchantId}/credentials (legacy, see section 4.2) | 100/min | service account |
10. Pub/Sub Events
The service publishes events to the provisioning-events topic for async consumers.
{
"eventType": "PROVISION_COMPLETED",
"jobId": "prov_job_abc123",
"merchantId": "loc_abc123",
"deviceSerial": "N5-2024-001234",
"action": "ACTIVATE",
"timestamp": "2026-03-08T15:00:12Z",
"steps": [
{ "step": "TRANSIT_ACTIVATION", "status": "COMPLETED", "durationMs": 5000 },
{ "step": "CREDENTIAL_STORAGE", "status": "COMPLETED", "durationMs": 1000 },
{ "step": "XTMS_PUSH", "status": "COMPLETED", "durationMs": 6000 }
]
}
Subscribers
| Subscriber | Event types | Purpose |
|---|---|---|
| Gateway Portal | All | Real-time status updates in UI |
| Callback delivery | PROVISION_COMPLETED, PROVISION_FAILED | POST to caller's callbackUrl |
| Alerting | PROVISION_FAILED, XTMS_PUSH_FAILED | PagerDuty alert for on-call |
4.3 Docs-side closeout checklist
Treat this page as closure-grade from the repo side only if readers can infer all of the following:
merchantIdin provisioning payloads means the concrete location id on the wire- CDE boundary and credential-handling rules are explicit
- support can reuse these examples without implying the old merchant-first tenant story is still normative
11. XTMS API Integration
The NexGO XTMS Cloud Open API (V1.9) provides full programmatic access for device provisioning.
Authentication
All XTMS API calls use SHA-256 signature-based authentication:
- Collect all request parameters (excluding
signature) - Sort by parameter name in ASCII/lexicographical order
- Concatenate as URL key-value pairs:
key1=value1&key2=value2... - Append the API key:
stringA + key - SHA-256 hash → uppercase hex string
Required per-request fields:
| Field | Description |
|---|---|
appId | Distributor identifier (provided by NexGO) |
key | API secret key (provided by NexGO) |
signatureMethod | Always SHA-256 |
signatureNonce | 13-digit timestamp (unique per request) |
timestamp | UTC time: yyyy-MM-dd'T'HH:mm:ssZ |
version | Always v1 |
Blocker: We need XTMS
appId+keycredentials from NexGO. Contact NexGO to enable API access for our distributor account.
Provisioning Flow (XTMS_PUSH step detail)
The XTMS_PUSH job step executes the following XTMS API calls in sequence:
Step 1: Create XTMS Merchant
POST /openapi/merchant/add
Request:
{
"merchantName": "Store ABC",
"merchantNo": "MID_12345", // TransIT merchantId
"terminalNos": ["TID_001", "TID_002"] // TransIT terminal IDs
}
Response:
{ "status": 200, "detail": 10086 } // XTMS merchant ID
Error codes: 1005001 (merchant already exists), 1005003 (TID duplicated)
Step 2: Bind Device to Terminal
POST /openapi/terminal/bindMachine
Request:
{
"merchantNo": "MID_12345",
"sn": "N5-2024-001234", // Physical device serial
"terminalNo": "TID_001"
}
Response:
{ "status": 200, "detail": null }
Rules: One device → one terminal. Device must belong to our distributor account.
Error codes: 1008003 (terminal already bound), 1008004 (device already bound), 1007 (SN not owned)
Step 3: Create Parameter File with TransIT Credentials
POST /openapi/param/addParamFile
Request:
{
"fileName": "transit_creds_MID_12345_TID_001",
"remark": "TransIT credentials for merchant MID_12345",
"spcParam": "{\"TRANSIT_DEVICE_ID\":\"88700000123456\",\"TRANSIT_TXN_KEY\":\"encrypted_key_value\"}"
}
Response:
{
"status": 200,
"detail": {
"id": 695,
"fileName": "transit_creds_MID_12345_TID_001",
"pkey": "ebf6deaaa3933c1ffffdd7eba20883da" // Used for subsequent updates
}
}
SPC = Specific Parameter Configuration. Custom key-value pairs delivered to the SmartConnect app. The
isEncryption: 1flag on parameter items hides values in the XTMS UI.
Step 4: Push Parameters to Device
POST /openapi/paramPush/pushBySN
Request:
{
"snList": ["N5-2024-001234"],
"appPackageName": "com.nexgo.smartconnect", // SmartConnect package name
"paramId": 695 // Parameter file ID from step 3
}
Response:
{ "status": 200, "detail": null }
Rules: Max 5000 SNs per push. Creates async task — device must be online to receive.
Step 5: Monitor Push Status
POST /openapi/paramPush/getPushDetail
Request:
{
"pushId": 2,
"sn": "N5-2024-001234",
"page": 1,
"pageSize": 10
}
Response:
{
"status": 200,
"detail": {
"processingMachineCount": 0,
"successMachineCount": 1,
"failMachineCount": 0,
"pageData": {
"rows": [
{
"sn": "N5-2024-001234",
"state": 3, // 3 = Success
"updateTime": "2026-03-08T15:00:12Z"
}
]
}
}
}
Push states: 0 Not requested, 1 Requesting, 2 Downloading, 3 Success, 4 Failed, 5 Canceled, 9 Ignored
Additional XTMS Endpoints Used
| Endpoint | Method | Purpose |
|---|---|---|
/openapi/merchant/updateInfo | POST | Update merchant name/contact info |
/openapi/merchant/detail | POST | Get merchant details by ID |
/openapi/terminal/unboundMachine | POST | Unbind device from terminal (deprovisioning) |
/openapi/terminal/getList | POST | List terminals and binding status |
/openapi/param/specific/update | POST | Update SPC parameters (credential rotation) |
/openapi/paramPush/cancelBySn | POST | Cancel pending push for a device |
/openapi/paramPush/closeById | POST | Close a completed push task |
/openapi/paramPush/getPushListBySN | POST | Get all push tasks for a specific device |
XTMS Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 1001 | General failure |
| 1005 | Distributor does not exist or lacks API permissions |
| 1006 | Signature verification failed |
| 1007 | SN does not match distributor ownership |
| 1008 | No permission for this interface |
| 1005001 | Merchant already exists |
| 1005002 | Merchant does not belong to distributor |
| 1005003 | TID is duplicated |
| 1005004 | Terminal already bound to a device |
| 1008003 | Terminal already bound to another merchant terminal |
| 1008004 | Merchant terminal already bound to another device |
| 1008006 | No authority to bind device to this terminal |
| 9999 | Unknown error |
| -1 | System exception |
Easy Deploy (Alternative for Bulk Provisioning)
XTMS Easy Deploy (V1.8+) bundles APP + PARAM + OTA + RKI tasks into a single template. Useful for fleet provisioning:
POST /openapi/easydpl/template/create // Create template (max 10 tasks)
POST /openapi/easydpl/push/create // Push template to devices by SN
POST /openapi/easydpl/push/list // List push tasks
POST /openapi/easydpl/push/detail // Track per-device status
Consider using Easy Deploy for initial fleet rollout (SmartConnect app + TransIT credentials + firmware in one push).
12. Blockers & Open Questions
| Item | Status | Impact |
|---|---|---|
| ✅ RESOLVED | XTMS Cloud Open API V1.9 provides 60+ endpoints. Full parameter push, merchant management, and device binding available. See §11 above. | |
| ✅ RESOLVED | SHA-256 signature with appId + key. See §11 Authentication above. | |
| XTMS API credentials | ❌ BLOCKED | Need appId + key from NexGO to authenticate XTMS API calls. Must contact NexGO to enable API access for our distributor account. |
| TransIT MerchantActivation API access | ❌ BLOCKED | Need developer portal access + sandbox credentials |
| Credential rotation impact | ❓ UNKNOWN | Does rotating a transactionKey in TransIT immediately invalidate the old one? If so, must coordinate with XTMS push timing to avoid downtime. |
| ✅ RESOLVED | XTMS supports multiple TIDs per merchant. Each TID binds to exactly one device SN. Our model supports this via the device_serial + metadata.terminalId fields. | |
| SmartConnect parameter key names | ❓ UNKNOWN | Need to confirm the exact SPC parameter keys that SmartConnect expects for TransIT credentials (e.g., TRANSIT_DEVICE_ID, TRANSIT_TXN_KEY). May need NexGO's help or reverse-engineering from SmartConnect sample app. |
13. Fallback: Manual XTMS Mode (Degraded)
If XTMS API is unreachable or credentials are unavailable, the XTMS_PUSH step falls back to a manual workflow:
- Provisioning job completes
TRANSIT_ACTIVATIONandCREDENTIAL_STORAGEsteps - Job status becomes
PENDING_MANUAL_PUSH - Gateway Portal displays the credentials (masked, copy-to-clipboard) to authorized admin
- Admin logs into XTMS web portal, configures device manually
- Admin clicks "Confirm Push" in Gateway Portal
- Job status becomes
COMPLETED - Audit log records: who viewed credentials, when, and when manual push was confirmed
This preserves the audit trail and keeps credentials within the CDE boundary. This mode is a degraded fallback, not the primary path — the XTMS API integration (§11) is the production flow.