PayinferenceDocs
payinference.com

Production integration guide

Everything on this page assumes the basic flow works (see Backend checkout integration) and covers what changes when real money and real traffic are involved.

text
Checkout page ──▶ Merchant backend ──▶ PayInference Decision API   (sync, tight budget)
                        │
                        ├──▶ PSP execution                          (your existing stack)
                        │
                        └──▶ Outcome reporting                      (async, queued)
  • Call PayInference from the backend service that owns payment execution, so the decision and the execution share a process and a log line.
  • Keep outcome reporting asynchronous: an in-process queue or your job system, with retries. It is idempotent per decision_id.
  • The browser never calls PayInference and never sees an API key.

Timeouts and fallbacks#

Give the decision call a hard budget and a plan for when it is exceeded. The server-side design budget is 50 ms; your client budget adds network time on top.

ts
const payinference = new PayInferenceClient({
  apiKey: process.env.PAYINFERENCE_API_KEY!,
  timeoutMs: 150, // hard budget for decide(); tune to your network
  reportingTimeoutMs: 2000, // outcomes, health reads
  retries: 2, // network-error retries for reporting calls only
  fallback: { provider: 'stripe', fallback_provider: 'adyen' },
});

With fallback configured, a timeout or network failure makes decide() return a locally built decision (action: "use_default_route", reason_codes including SDK_FALLBACK_USED, and sdk_fallback: true) instead of throwing. Checkout never waits on an outage.

Choose your own safe default; there is no universal one. If PayInference is unavailable, follow a default based on your own policy: for low-risk payments, continuing through your default provider is usually right; for high-risk payments, your policy may instead require a step-up, a pending state in your order system, or refusing the attempt. Encode that choice explicitly, whether via the SDK fallback option, or your own handler for PayInferenceTimeoutError and PayInferenceNetworkError.

Idempotency#

  • Send an Idempotency-Key per attempt on decision calls so client retries never act on two different decisions, deduplicate checkout submissions on order_id, and use your PSP's idempotency keys for the charge itself.
  • Outcome calls are idempotent per decision_id; retry them freely.

Details: Idempotency and retries.

Retries and circuit breaking#

  • Never retry HTTP error responses from PayInference; they are answers.
  • Retry network errors for reporting calls only. For decisions, prefer the fallback over retries: two timeout windows is checkout time you do not have.
  • Wrap the decision call in your standard circuit breaker. When the circuit is open, go straight to your safe default and skip the call entirely; the SDK fallback then never even waits for the timeout. Reset probing on your breaker's normal schedule.

Logging#

Log, at minimum, per attempt:

  • order_id, decision_id, action, route.primary_provider
  • reason_codes (they are safe to log; that is what they are for)
  • decision_latency_ms and your measured client-side latency
  • Whether the SDK fallback was used (sdk_fallback)
  • The x-request-id response header on failures

Never log the request payload wholesale into systems with weaker access control; it contains no card data by construction, but amounts and countries are still business data.

Outcome reporting at scale#

  • Queue outcome reports and flush asynchronously with backoff. Delivery matters more than immediacy.
  • Report failures as diligently as successes; failure outcomes drive provider health and retry intelligence.
  • Map PSP response codes to the canonical outcome values once, in one module, and pass the raw code in provider_reason_code.

Testing before go-live#

  • Integrate against a local instance (pnpm dev, seeded demo data) or your deployment's test environment with a pi_test_… key. Test-mode decisions are marked "mode": "test" and kept separate in analytics.
  • Simulate policies before publishing: POST /v1/policies/simulate.
  • Load-test the decision path with pnpm benchmark against your own deployment to validate the latency budget from your network position.

See Testing and local sandbox.

Operational monitoring#

Watch, and alert on:

Signal Why
Client-side decide latency p95 and timeout rate Your checkout budget; sustained timeouts mean the fallback is carrying traffic
Fallback usage rate (SDK_FALLBACK_USED in your logs) Nonzero is fine transiently; sustained means an availability problem
429 rate Traffic outgrew the configured per-key limit
Instruction mix (action distribution over time) A sudden spike in block or step_up usually means a policy or risk change
Outcome approval rate per provider The end-to-end measure of routing quality
GET /ready Liveness of a self-hosted deployment's dependencies

Security checklist#

  • API keys live in a secret manager, never in source, images, or frontend bundles
  • Separate keys per service and environment; least-privilege scopes (decision:write + outcome:write for checkout)
  • Test keys (pi_test_…) in staging, live keys (pi_live_…) in production, never mixed
  • Key rotation procedure exercised (create new, deploy, revoke old)
  • No card data, CVV, email, or address in any field you send; rely on the schema rejection but do not depend on it
  • Outcome and decision logs on your side treated as business-sensitive
  • x-request-id captured for support escalation

Go-live checklist#

  • Timeout and fallback behavior tested by blackholing PayInference in staging
  • Every instruction handled, including block, step_up, and unknown-action default
  • Outcome reporting verified end to end (check the decision log shows outcomes)
  • Policy published and simulated against representative transactions
  • Dashboards and alerts from the monitoring table above in place