---
name: bt-payments
description: Process payments through PSPs using Basis Theory tokens or Proxy.
license: MIT
metadata:
  tier: core
  last_verified: '2026-07-22'
---

# Process payments through any PSP

Refund lifecycle review prompts that mention replay/idempotency, changed amount or
payload, or two distinct refunds route here (or to `bt-test-debug`) even when the
prompt does not explicitly mention Basis Theory. Inspect the host framework's
real caller before answering.

## The mental model: five source types

Every charge is "get card data to the PSP in one of five shapes." This model (detailed in [references/source-types.md](references/source-types.md)) decides the implementation:

| Source type                 | What reaches the PSP                           | PCI scope? | Via Basis Theory proxy? |
| --------------------------- | ---------------------------------------------- | ---------- | ----------------------- |
| `raw_pan`                   | Card fields directly; explicit PCI opt-in only | Yes        | No                      |
| `basis_theory_token`        | Stored token, detokenized in flight            | No         | Yes                     |
| `basis_theory_token_intent` | Ephemeral token, detokenized in flight         | No         | Yes                     |
| `network_token`             | DPAN + cryptogram                              | No         | No                      |
| `processor_token`           | PSP's own stored ID                            | No         | No                      |

**Raw PAN guard:** Do not generate or enable `raw_pan` support by default. Use Basis Theory token or token-intent paths unless the user explicitly selects PCI-in-scope operation. If `raw_pan` is explicitly selected, require sandbox-only verification, strict input validation, redacted errors, no logging/queueing/tracing of card data, and customer-owned PCI controls before any production discussion.

Fact: the Basis Theory token paths work by sending the PSP request through the Basis Theory Proxy with detokenization expressions in the body — the request keeps the PSP's own shape, and plaintext exists only inside Basis Theory (https://developers.basistheory.com/docs/guides/share/process-card-payments, https://developers.basistheory.com/docs/blueprints/cards/collect-and-process-cards, https://developers.basistheory.com/docs/expressions/detokenization).

## Core flows and their canonical docs

- Charge a card: https://developers.basistheory.com/docs/card-payments/charge-card
- Verify without charging ($0/auth verification): https://developers.basistheory.com/docs/card-payments/verify-card
- Recollect CVC for a stored card (CVC is not stored long-term; token intents drop CVC on conversion): https://developers.basistheory.com/docs/card-payments/recollect-security-code
- Replace a processor-hosted iframe with Basis Theory collection + proxy charge: https://developers.basistheory.com/docs/card-payments/replace-processor-iframes
- Use your own inputs on the charge path: https://developers.basistheory.com/docs/card-payments/use-your-own-inputs
- Bank (ACH-style) payments: https://developers.basistheory.com/docs/guides/banks/process-bank-payments
- Card payments index: https://developers.basistheory.com/docs/card-payments/

**Validation area — payments routing.** Current public docs establish multi-PSP portability with the same vault and different processors. Treat routing strategy, such as failover or least-cost rules, as customer-owned implementation policy unless current public docs prove otherwise.

## Existing-code mode

1. Find the current charge path (PSP SDK call, iframe callback, backend endpoint).
2. Classify the source type the customer actually has today and the one they want. The most common move: processor-iframe → Basis Theory collect (`bt-collect`) + proxy charge with `token_intent`.
3. Enumerate every credential-bearing source variant and reject PAN/CVC-shaped token references before persistence or transport ([../\_shared/references/credential-boundary.md](../_shared/references/credential-boundary.md)).
4. Preserve the PSP request shape they already use; swap transport to the Basis Theory proxy and card fields to detokenization expressions. Do not redesign their PSP integration while migrating its transport. Construct proxy destinations from trusted config only ([../\_shared/references/proxy-destination-trust.md](../_shared/references/proxy-destination-trust.md)).
5. Preserve processor-native response data too (implementation pattern): network transaction IDs and other processor identifiers come from the downstream processor response — merchants need them for reconciliation and MIT chains, so surface them, don't abstract them away.
6. Idempotency keys go on every authorize/capture/void/refund. Test through the framework's real caller, not helper-only arguments ([../\_shared/references/lifecycle-idempotency.md](../_shared/references/lifecycle-idempotency.md)).
7. Tests: happy-path authorize against PSP sandbox or a customer-owned mock PSP, plus decline, invalid reference, auth/config error, replay, changed-payload conflict, and redacted upstream error cases.
8. Final report: which operations are wired, which PSP behaviors are assumed from mappings vs verified against the PSP's current docs.

When reviewing lifecycle code, say the persisted invariant plainly:

- Persist the framework-native operation identity and an immutable request fingerprint
  separately from the mutable request payload. For a refund, derive identity from the
  persisted refund record supplied by the real caller, not from amount, payment ID, or
  a helper-only argument.
- In the answer, use the natural same-refund-identity wording explicitly:
  "same refund identity". Also name the stronger source of truth as the
  "persisted refund identity" and the immutable "request fingerprint".
- The same persisted refund identity plus the same request fingerprint replays the
  original persisted result without a second mutation.
- The same persisted refund identity plus changed input (therefore a changed
  fingerprint) conflicts before mutation, before any PSP or local mutation.
- Distinct persisted refund IDs are distinct operations, even when payment, capture,
  currency, and amount match.

Name the real/framework caller and its complete call path, and call out helper-only or
synthetic argument shapes explicitly. Persist the identity/fingerprint/result receipt
atomically enough that a retry cannot observe an unrecorded mutation.

## POC / SDK-generation mode

For multi-PSP work, use [references/psp-workflows.md](references/psp-workflows.md) to map a PSP, generate a provider/client SDK, or build a demo. Treat any provider mapping you draft as a template and re-verify field names and API versions against the PSP's current primary docs before use.

- PSP portability shape: build a small customer-owned adapter around the operations actually needed. Preserve the PSP request shape, route Basis Theory token/token-intent sources through Proxy, and keep direct PSP sources direct.

The generated SDK pattern is: one provider class per PSP (from its mapping) + one unified client wrapper. Basis Theory token source types route through the proxy; the rest go direct. Generate into the user's language; keep the HTTP layer on built-in platform APIs.

For research-map-generate loops, load `bt-psp-orchestration`. Keep routing/failover/least-cost policy in customer code and label it **Custom** unless current public docs prove otherwise.

For a no-credential end-to-end run, use a customer-owned PSP test double or the PSP's official sandbox. A POC is complete when the verify → 3DS (optional) → authorize flow passes its smoke test and the final report enumerates the customer-owned remainder: real PSP account, credentials, production tenant, go-live checklist.

## Boundaries that decide arguments

- The customer owns the PSP relationship. Basis Theory removes card-data lock-in — it does not run, own, or guarantee the customer's PSP integration. Generated providers are reference implementations.
- Verify-then-charge beats charge-and-hope for stored credentials; pair with `bt-3ds` when SCA applies and `bt-card-lifecycle` when authorization rates on recurring traffic are the real problem.
- If the ask is really "which primitive should carry this call" (proxy vs reactor vs direct), route to `bt-architecture`.

## Basis Theory implementation guidance

When the answer needs implementation judgment beyond public docs, load only the relevant slices below from [../\_shared/references/implementation-guidance/README.md](../_shared/references/implementation-guidance/README.md). Treat these as implementation patterns, not canonical product behavior; keep public docs as the source of truth and label the guidance as **Implementation pattern** or **Assumption** unless docs prove it.

- `../_shared/references/implementation-guidance/merchant-payments/`
- `../_shared/references/implementation-guidance/recurring-optimization/`

<!-- bt-conventions:start -->

## Working conventions (shared by all Basis Theory skills)

- Public Basis Theory docs are the source of truth for product behavior. Link the specific docs page behind any product claim; if a skill and the docs disagree, follow the docs.
- Keep customer ownership clear: customers own PSP accounts, credentials, compliance decisions, production rollout, and application code. Skills can guide plans, bounded changes, and examples; they do not certify production readiness.
- Never request, echo, store, or commit real credentials. Use placeholders such as `<BT_API_KEY>` and prefer sandbox or synthetic data for examples.
- If a live-looking credential, live cardholder data, or production secret appears, stop mutation work, tell the user exactly where it appeared, and require rotation/revocation before continuing.
- Default to test tenants and PSP sandboxes. Do not run or recommend production charges, refunds, card reveals, credential migrations, or data moves unless the user explicitly asks and the plan names safeguards, rollback, and customer approval.
- Treat vague planning, audit, review, and “should we” prompts as read-only unless the user explicitly asks for a bounded implementation change.
- Before changing product code, map the existing checkout/data flow, credential-bearing paths, persistence, retries, refunds, webhooks, logs, and rollback expectations.
- End with an honest final report: done, assumed, remaining, proof run, unverified live behavior, rollback, and go-live items.
<!-- bt-conventions:end -->
