Skip to main content

CardPointe Staging Activation Runbook

1. Purpose & scope

This runbook activates the Fiserv CardPointe rail on staging end to end: provision UAT credential profiles, route a location onto them, run the cardpointe-activation cert-runner tab against the public staging API, verify host-side results, and (after card evidence is recorded) enable ACH platform-wide.

It is executed by a human with staging management access. The implementer of the CardPointe code does not need staging credentials — everything below is copy-paste executable.

Related pages: CardPointe Integration (architecture, endpoints, credential shape).

Evidence handling (read first)

Raw request/response logs, cert-runner output directories, and exported evidence belong in the run folder (--output-dir) and its GCS upload location only. Never paste raw gateway or CardPointe payloads into chat, PR descriptions, or issues — reference the run folder path or the GCS URI instead. This mirrors the TransIT certification evidence policy.

2. Prerequisites

  • Staging management API access with super-admin (admin:*) scope and a management OAuth client (client_id/client_secret).
  • The pilot staging organizationId and a locationId under it.
  • CardPointe UAT sandbox MIDs (already issued): 800000070034 (PeakPOS IC Plus) and 800000070035 (PeakPOS Surcharge-enrolled), username testing, UAT password from the team vault.
  • A checkout of the monorepo to build the cert runner: bazel build //tools/certification:runner.
  • An enabled bolt-auth-key Secret Manager version in both peakgateway-staging and pinpoint-gateway, verified by metadata only as described in Step 0. Do not enable CardPointe in either environment without this gate.

Staging URLs used throughout:

SurfaceURL
Public APIhttps://staging-pay.peakgateway.co
Public management APIhttps://staging-api.peakgateway.co/management
OAuth token endpointhttps://staging-api.peakgateway.co/oauth2/token
CardPointe UAT portalhttps://cardpointe-uat.cardconnect.com

Token minting note: always mint OAuth tokens against the load-balancer URL (https://staging-api.peakgateway.co/oauth2/token), not a direct Cloud Run service URL — direct-service tokens fail audience validation behind the LB (same gotcha as the TSYS cert runs). The cert runner does this for you when you pass --oauth-client-id/--oauth-client-secret; it derives the staging token URL from --public-api-url.

3. Activation sequence

Step 0 — Gate CardPointe on the Terraform-managed Bolt secret

Terraform owns the CMEK-protected bolt-auth-key container, the card-present accessor grant, and the BOLT_AUTH_KEY Cloud Run reference. Operators own secret versions out of band. Never inspect or print the payload.

Before changing cardpointe_enabled in either environment, both commands below must return at least one ENABLED version:

gcloud secrets versions list bolt-auth-key \
--project=peakgateway-staging \
--filter='state=ENABLED' \
--sort-by='~createTime' \
--limit=1 \
--format='table(name.basename(),state,createTime)'

gcloud secrets versions list bolt-auth-key \
--project=pinpoint-gateway \
--filter='state=ENABLED' \
--sort-by='~createTime' \
--limit=1 \
--format='table(name.basename(),state,createTime)'

An empty result is a hard stop. Obtain the Fiserv-issued key through the approved secure channel and follow Bolt auth-key rotation to upload a version with --data-file=<secure-file>; do not pass the value through Terraform, a shell argument, a portal form, a ticket, or this runbook.

After enabling CardPointe and rolling gateway-card-present, inspect the deployed references:

gcloud run services describe gateway-card-present \
--project=peakgateway-staging \
--region=us-east1 \
--format='yaml(spec.template.spec.containers[0].env)'

gcloud run services describe gateway-card-present \
--project=pinpoint-gateway \
--region=us-east1 \
--format='yaml(spec.template.spec.containers[0].env)'

For each enabled environment, confirm BOLT_ENABLED is "true" and BOLT_AUTH_KEY uses secretKeyRef secret bolt-auth-key, version alias latest. This validates metadata and wiring only. A rollout is required to resolve a newly uploaded highest-numbered version. The alias does not fall back when that version is disabled.

In staging, prove the value only by opening a real Bolt session to an online HSN authorized for the supplied location:

curl --fail-with-body -X POST \
"https://staging-pay.peakgateway.co/card-present/api/v1/card-present/bolt/terminals/${BOLT_HSN}/ping" \
-H "Authorization: Bearer ${STAGING_DEVICES_READ_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"merchantId\":\"${STAGING_LOCATION_ID}\"}"

Proceed only when the response reports reachable=true. A 503 naming Bolt authentication means the configured version was rejected; reachable=false proves only a device-level rejection and does not validate the key. Keep the prior version enabled until this check succeeds. If rollback is required, follow the Bolt auth-key rotation procedure: upload the known-good value as a new version and roll again rather than expecting latest to fall back. Do not send automated live production terminal traffic; repeat the production metadata/reference gate before production enablement and use the approved production validation window.

Step 1 — Create the staging credential profiles

Create one profile per UAT MID via the management API. Payload keys inside credentialPayload are exactly the typed CardPointeProcessorCredentials field names: merchid, username, password, optional environment / baseUrl.

curl -sS -X POST "https://staging-api.peakgateway.co/management/api/v1/organizations/${ORG_ID}/credential-profiles" \
-H "Authorization: Bearer ${MGMT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "CardPointe UAT IC Plus",
"processorType": "FISERV",
"processorPlatform": "CARDPOINTE",
"acceptsCardPresent": true,
"acceptsCardNotPresent": true,
"acceptsAch": true,
"credentialPayload": {
"merchid": "800000070034",
"username": "testing",
"password": "<UAT password from vault>",
"environment": "SANDBOX"
}
}'

Repeat with "name": "CardPointe UAT Surcharge" and "merchid": "800000070035" for the surcharge-enrolled profile, keeping "acceptsAch": false for that profile. Record both returned credentialProfileId values. The primary IC Plus profile above must carry "acceptsAch": true, and the selected processor adapter must advertise achSale for the ACH route.

Step 2 — Route the pilot location onto CardPointe

curl -sS -X PUT "https://staging-api.peakgateway.co/management/api/v1/organizations/${ORG_ID}/locations/${LOCATION_ID}/processor-routes/CARD_NOT_PRESENT" \
-H "Authorization: Bearer ${MGMT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"credentialProfileId": "<credentialProfileId from step 1>"}'

The primary IC Plus profile needs its own ACH route in addition to the card-not-present route. Add it before running the ACH smoke:

curl -sS -X PUT "https://staging-api.peakgateway.co/management/api/v1/organizations/${ORG_ID}/locations/${LOCATION_ID}/processor-routes/ACH" \
-H "Authorization: Bearer ${MGMT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"credentialProfileId": "<primary CardPointe IC Plus credentialProfileId from step 1>"}'

Step 3 — Validate the credentials

The processing service exposes an internal, non-mutating credential probe (safe: it performs an order-inquiry against the MID and interprets only the authentication outcome):

# From a workload with ROLE_INTERNAL_SERVICE access (e.g. via the internal service proxy):
curl -sS "http://processing/api/v1/internal/credential-profiles/${CREDENTIAL_PROFILE_ID}/cardpointe-validation"

Expected: a response indicating the credentials authenticate. An authentication failure here means the credentialPayload is wrong — fix it via PATCH .../credential-profiles/{credentialProfileId} before proceeding.

Step 4 — Run the cardpointe-activation cert-runner tab

bazel build //tools/certification:runner
bazel-bin/tools/certification/runner run \
--processor CARDPOINTE \
--tab cardpointe-activation \
--public-api-url https://staging-pay.peakgateway.co \
--organization-id "${ORG_ID}" \
--location-id "${LOCATION_ID}" \
--cardpointe-mid 800000070034 \
--oauth-client-id "${CERT_OAUTH_CLIENT_ID}" \
--oauth-client-secret "${CERT_OAUTH_CLIENT_SECRET}" \
--output-dir ./cert-results/cardpointe-activation-$(date +%Y%m%d)

The tab drives the public gateway API only (virtual-terminal, transaction, and transaction-verification routes) — it never calls CardPointe directly. Step sequence:

StepAction
cpa1Visa sale $10.10 (initial card-on-file storage — token funds cpa7)
cpa2 / cpa2cVisa auth $15.00 → full capture $15.00
cpa3 / cpa3cMasterCard auth $20.00 → partial capture $12.00
cpa4 / cpa4vVisa sale $25.00 → void
cpa5Full refund $15.00 of cpa2c
cpa6Partial refund $5.00 of cpa3c
cpa7Token-funded follow-on sale $30.00 (saved credential from cpa1)
cpa7iInquire verification of cpa7 (GET /api/v1/transactions/{id})
cpa8 / cpa8cVisa auth $18.00 → tip-adjust capture $21.00 (capture above auth; CardPointe's documented tip path)

Timing note: CardPointe refunds settle-side transactions; if cpa5/cpa6 fail with a not-settled host response, wait for the UAT auto-settlement window and resume with --start-step cpa5 --output-dir <same dir> (the runner persists state per output directory).

To exercise the surcharge-enrolled MID, re-run steps 1–4 with --cardpointe-mid 800000070035 against a location routed onto the surcharge profile and confirm the host-applied fee in the UAT portal (the gateway sends no surcharge field — CardPointe auto-applies per enrollment).

Step 5 — Verify host-side and ingestion-side results

CardPointe settlement is report-only: this sweep reads funding/settlement reports and never issues a BatchClose command. Public settlement resources remain read-only; host evidence resolves the batch to SETTLED or REVIEW_REQUIRED.

  1. Log into https://cardpointe-uat.cardconnect.com and verify every step above shows the expected approval/void/refund state under the right MID. Cross-check retref/respcode against the run folder evidence.

  2. Trigger the funding/settlement ingestion sweep and verify durable rows land:

    # From a workload with ROLE_INTERNAL_SERVICE access:
    curl -sS -X POST "http://processing/api/v1/internal/scheduled/cardpointe-funding-ingestion"

    Then confirm processor_funding_events rows exist for the credential profile (and that a second trigger inserts no duplicates — ingestion is idempotent). Settlement-status reconciliation rows arrive via the nightly reconciliation sweep once the UAT batch settles.

  3. Upload the run folder to the cert-runs GCS bucket and record the URI — it is the certificationEvidence reference for step 6.

Step 6 — Post-smoke: record ACH administrative evidence

The platform ACH_TENDER row is retained for administrative/audit evidence and command history; it is not a runtime prerequisite for ACH. A super-admin may record the evidence through management:

# Platform enable (requires certificationEvidence — the GCS URI or ticket from step 5):
curl -sS -X PUT "https://staging-api.peakgateway.co/management/api/v1/external-capabilities/ACH_TENDER" \
-H "Authorization: Bearer ${MGMT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"provider": "FISERV_CARDPOINTE",
"requestKey": "ach-tender-staging-enable-1",
"reason": "CardPointe staging activation smoke passed",
"certificationEvidence": "<GCS URI or ticket from step 5>"
}'

The merchant/location gates remain the credential profile and route: a location only advertises and accepts ACH when its resolved profile has acceptsAch: true, its ACH route selects that profile, provider credentials resolve, and the selected adapter advertises achSale=true. ACH_TENDER records platform-level administrative/audit state; it does not gate runtime ACH reads.

Then re-run the tab with the ACH smoke group (ECHK checking sale + ESAV savings auth):

bazel-bin/tools/certification/runner run \
--processor CARDPOINTE --tab cardpointe-activation --enable-ach-steps \
--public-api-url https://staging-pay.peakgateway.co \
--organization-id "${ORG_ID}" --location-id "${LOCATION_ID}" \
--oauth-client-id "${CERT_OAUTH_CLIENT_ID}" --oauth-client-secret "${CERT_OAUTH_CLIENT_SECRET}" \
--output-dir ./cert-results/cardpointe-ach-$(date +%Y%m%d)

The ACH group requires the location's credential profile to have acceptsAch: true, a matching ACH route, provider credentials, and an adapter with achSale=true; --enable-ach-steps is off by default so the first, card-only run never sends ACH traffic. The platform capability row above is administrative/audit metadata only.

4. Bolt hardware UAT gate

Card-present (Bolt) activation is not covered by this runbook and remains a separate, hardware-gated milestone:

  • Device shipment: UAT terminals (Clover Flex/Mini or Ingenico Lane) must be received and registered; record HSNs in the device inventory.
  • HSN inventory: every bolt_terminals binding must reference a physically-present HSN.
  • Session TTL / cancel verification: on real hardware, verify session-key expiry behavior and that cancel interrupts an in-flight authCard prompt (the three-state APPROVED/DECLINED/UNKNOWN contract can only be signed off against a physical terminal).

Until this gate passes, bolt.enabled stays false in card-present staging config.

5. Fiserv outreach checklist

Items not answerable from public CardPointe documentation. Each row lists the exact question for Fiserv, the gateway feature blocked on it, and what flips when it is answered. Capability flags stay false until the answer plus (where applicable) certification evidence lands.

#Vendor questionBlocked gateway featureFlips when answered
1What is the documented Gateway request contract for a force/offline auth with a voice authcode on /auth?Force auth on the CardPointe railProcessorCapabilities.cardPointeDefault().forceAuth + adapter forceAuth implementation
2Is PIN debit supported on the Gateway /auth (card-not-present host path), and with which fields? Terminal-level includePIN/aid is documented; the Gateway path is not.Gateway-level PIN debit routingpinDebit capability flag
3Is EBT supported on the CardPointe Gateway, and with which tender/field contract?EBT tendersebt capability flag
4Is incremental authorization supported (auth top-up on an open auth), and via which endpoint?Incremental authincrementalAuthorization capability flag
5What is the soft-descriptor contract (field names, length limits, per-network behavior) on /auth//capture?Merchant soft descriptorsNew optional request fields on the CardPointe client + adapter mapping
6What is the authoritative CAU (/cardconnect/rest/v2/updater) JSON request/response schema and the outcome wire codes?Card Account Updater ingestion (models are collection-derived, TODO(vendor-verify))ACCOUNT_UPDATER external capability activation gate; CAU model KDoc TODOs removed
7What is the daily funding-file availability window, and what does a /funding HTTP 404 mean (no data yet vs. error)?Funding ingestion scheduler correctnessCloud Scheduler cron for cardpointe-funding-ingestion; 404 handling in CardPointeFundingIngestionService
8Bolt tip endpoint version: docs say /v2/tip, the Postman collection says v3 — which is current?Bolt tip promptBoltClient.tip path constant
9What is the numeric Bolt session-key TTL (and idle vs. absolute expiry semantics)?Bolt session lifecycle tuningSession refresh margin in BoltTerminalService
10Confirm surcharge auto-apply behavior on UAT MID 800000070035: is the fee applied host-side per enrollment with no request field, and how does it appear on /settlestat?Surcharge correctness (gateway sends no surcharge field by design)Surcharge section of the parity doc; surcharge-MID leg of this runbook
11How are ACH SEC codes (PPD/CCD/WEB/TEL) assigned on Gateway ACH transactions — derived by the host or settable per request?ACH SEC-code fidelityACH field mapping in the adapter; parity doc §4
12Confirm partial-void semantics for debit: docs state a partial void of a debit transaction voids the FULL amount — is this current for UAT and production?Partial void UX (callers must read the actual voided amount)Adapter KDoc + partialVoid capability note
13What are the Apple Pay / Google Pay certification requirements for CardSecure tokenizeDevice (EC_GOOGLE_PAY, RSA-packaged Apple token) before production enablement?Wallet sale/auth on CardPointewalletSale/walletAuthorization capability flags
14How are per-merchant production tokenizer subdomains assigned (cardpointe.tokenizer.site config used by hosted checkout)?Production hosted-checkout tokenizationcardpointe.tokenizer.site in services/online-txn production config

Send as a single case to the Fiserv integration contact; track answers on the CardPointe parity doc and link the case ID here once opened.