Skip to main content

TransIT Certification Run Runbook

Audience: Anyone who needs to re-run the TransIT v6.2 cert suite (Case 00230402) against the sandbox or production merchant credentials.

Tool: tools/certification/ (Kotlin cert runner) + tools/certification/run-all-tabs.sh (orchestration wrapper).

Placeholder convention: the commands below contain literal placeholders wrapped in angle brackets (e.g. <from gcloud projects list>, <timestamp>, <Greg's email at Global Payments>, <YYYY-MM-DD>). Fill these in at run time --- they are intentional, not TODOs.

What this runbook covers

  • How to obtain credentials for the cert runner.
  • How to run a single tab or all three tabs back-to-back.
  • How to handle common failure modes per step.
  • How to package evidence and submit to Global Payments.

The In-App tab remains in the runner for scope-dependent certification work, but it is not part of the default automated sequence. Run it explicitly with --tabs in-app or CERT_TABS=in-app only when TSYS confirms it is in scope.

Red rows are skippable

Greg Crosby at Global Payments marks rows in the cert Excel script red to indicate they do not apply to Pinpoint POS. The tab definitions in tools/certification/src/main/kotlin/com/myriad/gateway/certification/tabs/*.kt already exclude red rows inline via // SKIP (red) comments next to every omission; no additional filter is needed. When a spec revision marks an additional row red, update the relevant tab .kt file to drop it and add a // SKIP (red) comment next to the removal.

Verify the inline markers still exist:

grep -c 'SKIP.*(red)' tools/certification/src/main/kotlin/com/myriad/gateway/certification/tabs/*.kt

Each tab file that has red rows in the spec should report a nonzero count.

Local-only dry-run prep

Use this checklist when preparing the cert runner without live gateway services, external TransIT calls, or Terraform:

# Confirm the wrapper parses options without requiring env or network access.
./tools/certification/run-all-tabs.sh --help

# Build the runner locally. Bazel may use the configured remote build cache,
# but this does not call gateway, management, TransIT, or Terraform.
bazel build //tools/certification:runner

# Run the local MockWebServer-backed tests.
bazel test //tools/certification:cert_client_test \
//tools/certification:route_policy_test \
//tools/certification:step_runner_test

# Re-check that red-row omissions are still documented in the tab definitions.
grep -c 'SKIP.*(red)' tools/certification/src/main/kotlin/com/myriad/gateway/certification/tabs/*.kt

Do not run the following during a local-only prep pass:

  • tools/certification/scripts/check-sandbox-connectivity.sh — curls the public API and management API.
  • tools/certification/run-all-tabs.sh without --help — runs setup and certification tabs unless you provide real skip/tab options, and the tab runner still submits transaction requests.
  • bazel run //tools/certification:runner -- setup or -- run — activates locations, imports credentials, creates sessions, and/or sends transaction traffic.
  • tools/certification/scripts/package-run.sh — uploads to GCS when gsutil is available.

Local end-to-end iteration loop

Use this flow when certification is close but a TSYS-required tag, saved-card path, or tab ordering bug still needs fast iteration. It runs the real services against the local emulators and the same certification runner code, then promotes only after the payloads are proven locally.

Which environment to use

Use the cheapest environment that can prove the question in front of you:

QuestionEnvironmentWhy
Does Kotlin build and do unit tests still pass?Local BazelNo network or payment side effects.
Does the runner produce the right tab/step sequence?Local stackProves orchestration and run-state behavior.
Does checkout save a real database-backed payment method?Local stackProves customer/token storage without spending staging transactions.
Does the TransIT request contain all required tags?Local stack or staging dry-run/log exportThe validator can catch missing tags before analyst review.
Does TSYS approve and return host reference numbers?Staging against TransIT UATRequires the real TransIT endpoint.
Did settlement pass after batch close?Staging against TransIT UATRequires the TSYS :30 batch push.
Does Apple Pay show the native sheet on iPhone?Deployed HTTPS checkout originApple wallet requires a trusted web origin and device wallet state.

What local can prove

  • The cert runner builds the correct tab sequence, start step, stop step, and resume state.
  • Checkout/session flows create customers, save cards, and reuse stored payment methods through the database instead of temporary files.
  • Processing builds the TransIT request DTOs with required terminal, industry, brand, Level II, Level III, card-on-file, void, and tokenization fields.
  • The raw request body shape can be compared against TSYS requirements before sending another staging transaction.

What local cannot prove

  • Real TransIT approvals, declines, host reference numbers, settlement, or batch close status.
  • Cloud Run IAM, deployed Secret Manager wiring, VPC/networking, or revision selection.
  • Apple Pay device-sheet behavior. Local tests can validate our request shape, but a real iPhone wallet flow must hit a deployed HTTPS checkout origin that Apple trusts.

Start the local stack

# From the repo root.
./scripts/dev.sh services
./scripts/dev.sh status

Wait until auth, management, online-txn, processing, and the Spanner emulator are healthy. If a service fails during startup, tail only that service:

./scripts/dev.sh logs processing
./scripts/dev.sh logs online-txn

Prefer /opt/homebrew/bin/bazel on Apple Silicon developer laptops if the shell does not resolve the expected Bazel binary:

/opt/homebrew/bin/bazel build //tools/certification:runner

Create local cert credentials

Mint or reuse a local API token that has both public checkout scopes and admin setup scope. For most developer loops, use the same OAuth client configured by the local auth service and keep the tokens in shell variables only:

export CERT_PUBLIC_API_URL="http://localhost:8184"
export CERT_PUBLIC_MANAGEMENT_URL="http://localhost:8183"
export CERT_TRANSIT_MID=888000003647
export CERT_TRANSIT_TERMINAL_NUMBER=75959504
export CERT_DEVICE_ID=88800000364701
export CERT_ORGANIZATION_ID=<local-organization-id>
export CERT_LOCATION_ID=888000003647
export CERT_API_KEY=<local-session-create-and-txn-process-token>
export CERT_MANAGEMENT_API_KEY=<local-admin-token>

Those URLs match the ./scripts/dev.sh services host-port mappings: online-txn on 8184, management on 8183. The cert runner's built-in local defaults use the same ports when no URL flags or env vars are provided.

If the local database is empty, create the organization, merchant, location, and TransIT credential profile through the management API or admin portal first. Then run setup once:

/opt/homebrew/bin/bazel run //tools/certification:runner -- setup \
--api-url "$CERT_PUBLIC_API_URL" \
--management-url "$CERT_PUBLIC_MANAGEMENT_URL" \
--api-key "$CERT_API_KEY" \
--management-api-key "$CERT_MANAGEMENT_API_KEY" \
--organization-id "$CERT_ORGANIZATION_ID" \
--location-id "$CERT_LOCATION_ID" \
--transit-mid "$CERT_TRANSIT_MID" \
--transit-terminal-number "$CERT_TRANSIT_TERMINAL_NUMBER" \
--device-id "$CERT_DEVICE_ID"

Do not put PAN, CVV, tokens, or raw TransIT payloads in shell history, chat, PR comments, or issue comments. Raw evidence belongs in the run folder or GCS only.

Run bounded slices

Use a run root outside tools/certification/cert-runs when iterating so the files are easy to keep and compare:

export RUN_ROOT="$HOME/Downloads/transit-local-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$RUN_ROOT"

Run the lowest-cost slice that exercises the bug:

# e-Commerce through the first batch close pause.
/opt/homebrew/bin/bazel run //tools/certification:runner -- run \
--api-url "$CERT_PUBLIC_API_URL" \
--management-url "$CERT_PUBLIC_MANAGEMENT_URL" \
--api-key "$CERT_API_KEY" \
--management-api-key "$CERT_MANAGEMENT_API_KEY" \
--organization-id "$CERT_ORGANIZATION_ID" \
--location-id "$CERT_LOCATION_ID" \
--transit-mid "$CERT_TRANSIT_MID" \
--transit-terminal-number "$CERT_TRANSIT_TERMINAL_NUMBER" \
--device-id "$CERT_DEVICE_ID" \
--tab ecommerce \
--stop-step ec11 \
--output-dir "$RUN_ROOT/ecommerce"

# Resume after the local or real settlement pause.
/opt/homebrew/bin/bazel run //tools/certification:runner -- run \
--api-url "$CERT_PUBLIC_API_URL" \
--management-url "$CERT_PUBLIC_MANAGEMENT_URL" \
--api-key "$CERT_API_KEY" \
--management-api-key "$CERT_MANAGEMENT_API_KEY" \
--organization-id "$CERT_ORGANIZATION_ID" \
--location-id "$CERT_LOCATION_ID" \
--transit-mid "$CERT_TRANSIT_MID" \
--transit-terminal-number "$CERT_TRANSIT_TERMINAL_NUMBER" \
--device-id "$CERT_DEVICE_ID" \
--tab ecommerce \
--start-step ec12_5 \
--output-dir "$RUN_ROOT/ecommerce"

For broad regression checks, run all in-scope tabs from scratch. This is slower, but it catches tab-specific differences such as Direct Marketing PHONE, Direct Marketing MAIL, recurring, installment, and card-on-file request values:

for tab in ecommerce moto recurring; do
/opt/homebrew/bin/bazel run //tools/certification:runner -- run \
--api-url "$CERT_PUBLIC_API_URL" \
--management-url "$CERT_PUBLIC_MANAGEMENT_URL" \
--api-key "$CERT_API_KEY" \
--management-api-key "$CERT_MANAGEMENT_API_KEY" \
--organization-id "$CERT_ORGANIZATION_ID" \
--location-id "$CERT_LOCATION_ID" \
--transit-mid "$CERT_TRANSIT_MID" \
--transit-terminal-number "$CERT_TRANSIT_TERMINAL_NUMBER" \
--device-id "$CERT_DEVICE_ID" \
--tab "$tab" \
--output-dir "$RUN_ROOT/$tab"
done

Validate request payloads before staging

The local run output proves gateway behavior. The final acceptance check should also validate the TransIT request payloads captured from service logs or dry-run artifacts:

tools/certification/scripts/validate-transit-required-fields.py \
--raw-log "$RUN_ROOT/raw-transit/ecommerce-transit-raw.json" \
--run-dir "$RUN_ROOT/ecommerce" \
--output "$RUN_ROOT/raw-transit/validation-ecommerce.txt"

When the validator reports a missing field, fix the production TransIT DTO or profile resolver. Do not patch the certification runner to add a cert-only tag. The runner must exercise the same path that hosted checkout and processing use.

Required-field validator workflow

The validator is the fast feedback loop for the analyst's API feedback and the Excel tab requirements. Run it against raw TransIT request logs before asking TSYS to review a tab.

tools/certification/scripts/validate-transit-required-fields.py \
--raw-log "$RUN_ROOT/raw-transit/ecommerce-transit-raw.json" \
--run-dir "$RUN_ROOT/ecommerce" \
--output "$RUN_ROOT/raw-transit/validation-ecommerce.txt"

tools/certification/scripts/validate-transit-required-fields.py \
--raw-log "$RUN_ROOT/raw-transit/moto-transit-raw.json" \
--run-dir "$RUN_ROOT/moto" \
--output "$RUN_ROOT/raw-transit/validation-moto.txt"

tools/certification/scripts/validate-transit-required-fields.py \
--raw-log "$RUN_ROOT/raw-transit/recurring-transit-raw.json" \
--run-dir "$RUN_ROOT/recurring" \
--output "$RUN_ROOT/raw-transit/validation-recurring.txt"

Expected result is zero errors. Warnings must be understood and written in the run notes. For example, missing serviceLocationGeoCoordinates is acceptable only when the merchant is not a traveling merchant; otherwise fix merchant or location metadata and rerun MasterCard transactions.

The validator should fail on production DTO problems, not cert-runner-only problems. If a required tag is missing, update one of these layers:

Missing field classFix location
Terminal/card-present tagsTransIT transaction profile resolver and request DTO mapping.
Brand fields such as MasterCard acceptor fields or Discover registrationBrand-specific profile mapping.
Level II or Level III fieldsCommercial data DTO defaults and serializer ordering.
Card-on-file fieldsStored-token/customer flow and TransIT COF mapping.
Void fieldsVoid request DTO and processing operation mapping.

Promote to staging

After local payload validation passes:

  1. Run focused Bazel tests for the touched service and Transit client.
  2. Build and push the affected service image.
  3. Deploy staging and confirm the expected Cloud Run revision is serving.
  4. Re-run the same bounded tab slice against staging.
  5. Export raw TransIT request/response logs from Cloud Logging into the run root.
  6. Re-run the required-field validator against the exported raw logs.
  7. Only then run the full three-tab certification pass and observe the TSYS :30 settlement wait before return/void steps.

Export staging raw logs

For staging evidence, export raw request/response logs from Cloud Logging into the run root. Keep these files private because TSYS requires the complete raw payloads and they may contain sensitive payment data.

export PROJECT_ID=peakgateway-staging # staging project (cert runs target staging)
export RUN_ROOT="$HOME/Downloads/stage-<run-id>"
export START_TIME="<YYYY-MM-DDTHH:MM:SSZ>"
export END_TIME="<YYYY-MM-DDTHH:MM:SSZ>"

mkdir -p "$RUN_ROOT/raw-transit"

gcloud logging read \
'resource.type="cloud_run_revision"
resource.labels.service_name="gateway-processing"
timestamp>="'"$START_TIME"'"
timestamp<="'"$END_TIME"'"
("TransIT request" OR "TransIT response")' \
--project "$PROJECT_ID" \
--format=json \
> "$RUN_ROOT/raw-transit/combined-transit-raw.json"

Then run the validator against the exported file:

tools/certification/scripts/validate-transit-required-fields.py \
--raw-log "$RUN_ROOT/raw-transit/combined-transit-raw.json" \
--run-dir "$RUN_ROOT" \
--output "$RUN_ROOT/raw-transit/validation-combined.txt"

The validator derives applicable checks from wrapper names, card brand, request fields, and optional runner context under --run-dir; it does not need an industry flag. Prefer tab-specific raw files for easier troubleshooting. Do not paste raw log contents into GitHub, Slack, email body text, or chat; upload the file through the TSYS Partner Portal or the approved evidence bucket.

Real-run timestamp capture

When doing an analyst-submittable run, record these values as the run happens:

ValueSource
Tab nameRunner tab name, for example eCommerce, MOTO, or Recurring.
MIDCert environment value, currently 888000003647.
Authorization startTimestamp of the first successful transaction request in the tab.
First batch close / pauseRunner output at the batch-close step.
Resume timeTimestamp when the post-:30 resume run starts.
Authorization endTimestamp of the final transaction before the tab is submitted.
First hostReferenceNumberFirst TransIT response host reference number in the tab.
Last hostReferenceNumberLast TransIT response host reference number in the tab.

Email format:

Hi <analyst>,

I completed the <tab name> tab and closed the batches manually. The first batch
was closed before the hourly :30 push window, then I waited until after the :30
settlement window before processing the return and return void steps.

Submission details:

Tab Name: <tab name>
MID: 888000003647
Authorization Start Date/Time: <Month D, YYYY H:MM:SS AM/PM timezone>
First Batch Close / Pause Time: <Month D, YYYY H:MM:SS AM/PM timezone>
Resume Time: <Month D, YYYY H:MM:SS AM/PM timezone>
Authorization End Date/Time: <Month D, YYYY H:MM:SS AM/PM timezone>
First hostReferenceNumber: <first hostReferenceNumber>
Last hostReferenceNumber: <last hostReferenceNumber>

I uploaded the complete raw TransIT request/response logs for this tab in the
Partner Portal.

Prerequisites

  1. Staging location 888000003647 must be active. The /api/v1/locations/{id} endpoint lives on the public management surface, so confirm via the public management URL:

    export CERT_PUBLIC_MANAGEMENT_URL="https://staging-api.peakgateway.co/management"

    curl -sS -H "Authorization: Bearer $CERT_API_KEY" \
    -H "X-Merchant-ID: 888000003647" \
    "$CERT_PUBLIC_MANAGEMENT_URL/api/v1/locations/888000003647"

    If not, create via the admin portal and run bazel run //tools/certification:runner -- setup ... once. The pre-flight script (tools/certification/scripts/check-sandbox-connectivity.sh) will consume CERT_PUBLIC_MANAGEMENT_URL too when set.

  2. OAuth client for the runner. Credentials in Secret Manager:

    • cert-runner-oauth-client-id
    • cert-runner-oauth-client-secret
  3. GCS bucket for evidence retention. gs://peak-cirunners-cert-runs/ with the GitHub Actions service account as roles/storage.objectCreator.

Full run (the happy path)

# 1. Export config
export CERT_PUBLIC_API_URL="https://staging-pay.peakgateway.co"
export CERT_PUBLIC_MANAGEMENT_URL="https://staging-api.peakgateway.co/management"

# 2. Fetch a fresh OAuth token
CLIENT_ID="$(gcloud secrets versions access latest --secret=cert-runner-oauth-client-id)"
CLIENT_SECRET="$(gcloud secrets versions access latest --secret=cert-runner-oauth-client-secret)"
export CERT_API_KEY="$(curl -sS -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d 'scope=session:create txn:process' \
"https://staging-api.peakgateway.co/oauth2/token" \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')"
export CERT_MGMT_API_KEY="$(curl -sS -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d 'scope=admin:*' \
"https://staging-api.peakgateway.co/oauth2/token" \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')"
export CERT_ORGANIZATION_ID=<gateway-organization-id>
export CERT_LOCATION_ID=<gateway-location-id>
export CERT_TRANSIT_MID=888000003647
export CERT_TRANSIT_TERMINAL_NUMBER=75959504
export CERT_DEVICE_ID=88800000364701

# 3. Pre-flight
./tools/certification/scripts/check-sandbox-connectivity.sh

# 4. Run the default automated tabs
./tools/certification/run-all-tabs.sh

Expected: the script runs setup and the requested tab range, each writing to tools/certification/cert-runs/full-<timestamp>/<tab>/. The runner pauses after in-tab batch closes. After the next TSYS :30 settlement window passes, resume from the printed next step with the same run root. At the end of all requested tabs, it generates cert-report.txt, archives the run as a .tar.gz, and uploads to GCS. It only emits a signed URL when CERT_RUN_SIGNURL_KEY is set to a service-account JSON key path; otherwise it prints the gcloud storage sign-url command to run manually.

Total wall clock: variable; settlement waits are operator-controlled. TransIT closes batches at 30 minutes past each hour.

TSYS review wants one tab per settlement. After each tab settlement, email the tab name, settlement date, and returned batch/settlement number to the TSYS reviewer.

Cloud Run Job per-tab real run

For a real certification pass, prefer one Cloud Run Job execution per tab with the evidence bucket mounted so each invocation appends to the same run root. Some tabs have dependent return/void steps after an in-tab batch close, and the runner must not continue those steps until TransIT reaches the next :30 settlement window. Resume e-Commerce after ec11 with CERT_START_STEP=ec12_5; resume MOTO after mo9 with CERT_START_STEP=mo10_1. If TSYS also asks for an hour between tabs, wait outside Cloud Run between tab executions.

One-time bucket mount

export PROJECT_ID=<gcp-project-id>
export REGION=us-east1
export CERT_JOB=<cloud-run-cert-job-name>
export CERT_JOB_SA=<cert-job-service-account@${PROJECT_ID}.iam.gserviceaccount.com>
export CERT_RUN_BUCKET=peak-cirunners-cert-runs

gcloud storage buckets create "gs://${CERT_RUN_BUCKET}" \
--project "$PROJECT_ID" \
--location "$REGION" \
--uniform-bucket-level-access

gcloud storage buckets add-iam-policy-binding "gs://${CERT_RUN_BUCKET}" \
--member "serviceAccount:${CERT_JOB_SA}" \
--role roles/storage.objectUser

gcloud run jobs update "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--add-volume name=cert-runs,type=cloud-storage,bucket="$CERT_RUN_BUCKET",mount-options="implicit-dirs=true" \
--add-volume-mount volume=cert-runs,mount-path=/mnt/cert-runs

The entrypoint honors CERT_RUN_ROOT; set it to a path under the mounted bucket. If CERT_RUN_ROOT is not set, the job writes under /tmp, which is ephemeral and is not suitable for split-tab runs.

Per-tab execution sequence

export PROJECT_ID=<gcp-project-id>
export REGION=us-east1
export CERT_JOB=<cloud-run-cert-job-name>
export RUN_ID="full-$(date -u +%Y%m%dT%H%M%SZ)"
export CERT_RUN_ROOT="/mnt/cert-runs/${RUN_ID}"

# 1. Setup + e-Commerce through the first batch close. This creates the shared evidence root.
gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_TABS=ecommerce,CERT_SKIP_SETUP=false"

# 2. Wait until the next TSYS :30 settlement window, then resume e-Commerce returns.
gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_TABS=ecommerce,CERT_SKIP_SETUP=true,CERT_START_STEP=ec12_5"

# 3. MOTO through the first batch close. Reuses the mounted evidence root and skips setup.
gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_TABS=moto,CERT_SKIP_SETUP=true"

# 4. Wait until the next TSYS :30 settlement window, then resume MOTO returns.
gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_TABS=moto,CERT_SKIP_SETUP=true,CERT_START_STEP=mo10_1"

# 5. Recurring.
gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_TABS=recurring,CERT_SKIP_SETUP=true"

Each invocation regenerates cert-report.txt from the state files already in CERT_RUN_ROOT, so after the final tab the bucket should contain:

gs://peak-cirunners-cert-runs/<RUN_ID>/
├── setup/
├── ecommerce/
├── moto/
├── recurring/
└── cert-report.txt

If a report needs to be regenerated without sending more transactions:

gcloud run jobs execute "$CERT_JOB" \
--project "$PROJECT_ID" \
--region "$REGION" \
--wait \
--update-env-vars "CERT_RUN_ROOT=${CERT_RUN_ROOT},CERT_REPORT_ONLY=true,CERT_SKIP_SETUP=true"

Partial runs

# Single tab
./tools/certification/run-all-tabs.sh --tabs moto

# Resume from a specific failed step
./tools/certification/run-all-tabs.sh --tabs ecommerce --start-step ec2_5

# Run through a specific step and pause
./tools/certification/run-all-tabs.sh --tabs ecommerce --stop-step ec11

# Skip setup (already activated)
./tools/certification/run-all-tabs.sh --skip-setup

# Skip the pre-flight (useful when iterating on a fix)
./tools/certification/run-all-tabs.sh --skip-preflight --skip-setup --tabs ecommerce

# Reuse an existing run root
./tools/certification/run-all-tabs.sh --run-root tools/certification/cert-runs/full-<timestamp> \
--skip-setup --tabs ecommerce --start-step ec12_5

# Scope-dependent In-App tab, only when TSYS confirms it is in scope
./tools/certification/run-all-tabs.sh --skip-setup --tabs in-app

Triage: what to do when a step fails

The runner marks the step FAILED and stops the tab. Inspect the JSON log in the run directory:

cat tools/certification/cert-runs/full-<timestamp>/ecommerce/<step-id>.json

Common patterns and fixes:

Response patternCauseFix
"responseStatus": 401Token expiredRe-fetch per "Full run" Step 2
"responseStatus": 403 on webhook endpointsToken missing scopeAdd webhook:manage to the public API token scopes
"responseStatus": 403 on /management/api/v1/...Management token missing admin scopeMint CERT_MGMT_API_KEY with admin:* and retry
"responseStatus": 404 on /api/v1/transactions/<id>/captureTransaction persistence is still in flightTiming issue --- rerun the step with --start-step <step-id>
Status = DECLINED when APPROVED expectedPossible real TransIT decline --- check response codeIf code is A0000 the card was approved but our status mapping is wrong. Fix in validateResponse in StepRunner.kt. If code is anything else, escalate to Greg
Status = APPROVED when PARTIAL_APPROVAL expectedPartial-auth flag not set in request bodyConfirm partialAuthSupport: true is in buildSaleBody for this step
NullPointerException in runnerResponse shape mismatch --- a field the runner expected was nullAdd null-safe extraction in the appropriate execute* method
Timeout on session creationonline-txn service cold startExtend session creation timeout in CertClient from 60s to 120s. The staging warm_services setting keeps min_instances >= 1 and fixes it permanently.

Submission to Global Payments

After a green full run (setup + e-Commerce, MOTO, and Recurring tabs):

./tools/certification/scripts/package-run.sh tools/certification/cert-runs/full-<timestamp>

This uploads the tarball to GCS and prints a signed URL (when CERT_RUN_SIGNURL_KEY points at a service-account JSON key). Email Greg Crosby (<Greg's email at Global Payments>) with:

  • Subject: Case 00230402 --- Pinpoint POS Cert Submission (run <timestamp>)
  • Body: a short summary, the signed URL, and a list of any steps that required notes.
  • Attachment: the filled-in cert Excel spreadsheet (populated by hand from cert-report.txt).