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.shwithout--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 -- setupor-- run— activates locations, imports credentials, creates sessions, and/or sends transaction traffic.tools/certification/scripts/package-run.sh— uploads to GCS whengsutilis 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:
| Question | Environment | Why |
|---|---|---|
| Does Kotlin build and do unit tests still pass? | Local Bazel | No network or payment side effects. |
| Does the runner produce the right tab/step sequence? | Local stack | Proves orchestration and run-state behavior. |
| Does checkout save a real database-backed payment method? | Local stack | Proves customer/token storage without spending staging transactions. |
| Does the TransIT request contain all required tags? | Local stack or staging dry-run/log export | The validator can catch missing tags before analyst review. |
| Does TSYS approve and return host reference numbers? | Staging against TransIT UAT | Requires the real TransIT endpoint. |
| Did settlement pass after batch close? | Staging against TransIT UAT | Requires the TSYS :30 batch push. |
| Does Apple Pay show the native sheet on iPhone? | Deployed HTTPS checkout origin | Apple 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 class | Fix location |
|---|---|
| Terminal/card-present tags | TransIT transaction profile resolver and request DTO mapping. |
| Brand fields such as MasterCard acceptor fields or Discover registration | Brand-specific profile mapping. |
| Level II or Level III fields | Commercial data DTO defaults and serializer ordering. |
| Card-on-file fields | Stored-token/customer flow and TransIT COF mapping. |
| Void fields | Void request DTO and processing operation mapping. |
Promote to staging
After local payload validation passes:
- Run focused Bazel tests for the touched service and Transit client.
- Build and push the affected service image.
- Deploy staging and confirm the expected Cloud Run revision is serving.
- Re-run the same bounded tab slice against staging.
- Export raw TransIT request/response logs from Cloud Logging into the run root.
- Re-run the required-field validator against the exported raw logs.
- Only then run the full three-tab certification pass and observe the TSYS
:30settlement 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:
| Value | Source |
|---|---|
| Tab name | Runner tab name, for example eCommerce, MOTO, or Recurring. |
| MID | Cert environment value, currently 888000003647. |
| Authorization start | Timestamp of the first successful transaction request in the tab. |
| First batch close / pause | Runner output at the batch-close step. |
| Resume time | Timestamp when the post-:30 resume run starts. |
| Authorization end | Timestamp of the final transaction before the tab is submitted. |
| First hostReferenceNumber | First TransIT response host reference number in the tab. |
| Last hostReferenceNumber | Last 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
-
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 consumeCERT_PUBLIC_MANAGEMENT_URLtoo when set. -
OAuth client for the runner. Credentials in Secret Manager:
cert-runner-oauth-client-idcert-runner-oauth-client-secret
-
GCS bucket for evidence retention.
gs://peak-cirunners-cert-runs/with the GitHub Actions service account asroles/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 pattern | Cause | Fix |
|---|---|---|
"responseStatus": 401 | Token expired | Re-fetch per "Full run" Step 2 |
"responseStatus": 403 on webhook endpoints | Token missing scope | Add webhook:manage to the public API token scopes |
"responseStatus": 403 on /management/api/v1/... | Management token missing admin scope | Mint CERT_MGMT_API_KEY with admin:* and retry |
"responseStatus": 404 on /api/v1/transactions/<id>/capture | Transaction persistence is still in flight | Timing issue --- rerun the step with --start-step <step-id> |
Status = DECLINED when APPROVED expected | Possible real TransIT decline --- check response code | If 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 expected | Partial-auth flag not set in request body | Confirm partialAuthSupport: true is in buildSaleBody for this step |
NullPointerException in runner | Response shape mismatch --- a field the runner expected was null | Add null-safe extraction in the appropriate execute* method |
| Timeout on session creation | online-txn service cold start | Extend 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).