High Wire Payments

Card payments

Charge cards from your server: authorize and capture in one call, authorize then capture or void, 3-D Secure, device data, and every decline.

The direct API takes card details from your server and charges them. Use it only when your environment is validated to handle card data. Otherwise collect the card in the browser with the Payment SDK, or hand the shopper a hosted checkout link and never touch the card at all.

Card data and PCI DSS

Sending card numbers to this API puts your systems in scope for PCI DSS (typically SAQ D). Use test cards only in examples and tooling, and never put real card data in a shell, its history, logs, tickets or chat. Never log request bodies, never store the security code, and keep card data in memory only for as long as the request needs it.

FlowEndpointsWhen
Authorize and capturePOST /v1/payments/auth-and-captureThe common case: charge the card now.
Authorize, then capture or voidPOST /v1/payments/auth, then POST /v1/payments/{id}/capture or POST /v1/payments/{id}/voidHold the funds first (stock check, manual review, charge on shipment), then settle or release them.

Both charge routes take the same request body and answer the same shape. Money that was captured is returned with a refund; an authorization that was not captured is released with a void.

Availability. Not every account can use the direct card routes; High Wire confirms at onboarding which surfaces yours supports. A route your account cannot use answers a charge decline that says so, see Declines.

Request body

Shared by auth-and-capture and auth.

FieldTypeRequiredRules
amountintegeryesCents, at least 1. 7200 is $72.00.
cardNumberstringyes12 to 19 digits, no spaces or separators.
cardExpirationstringyesMMYY: 1133 is November 2033.
cardCvvstringyesThe card's security code. Never stored.
cardholderNamestringyesName on the card.
cardholderEmailstringyesThe customer's e-mail, trimmed and lowercased. When the card is saved, this is what identifies the customer. The format is not validated.
cardholderPhonestringyesE.164 recommended, for example +14155550101.
cardholderBillingAddressstringyesStreet address.
cardholderBillingZipcodestringyesPostal code.
cardholderBillingCitystringyesCity.
cardholderBillingStatestringyesTwo-letter US state code.
savePaymentMethodForFutureUsebooleannoDefault false. true saves the card and returns customerId and paymentMethodId. Sending it attests that you have the customer's consent. See Saved payment methods.
deviceDataobjectnoBrowser signals for your payment processor, validated strictly when present. See Device data.
riskSessionKeystringnoThe value riskSessionKey() returns on the Payment SDK's device-screening collector (HighWireCollect({ publishableKey, apiBase })), forwarded unchanged (at most 100 characters). Omit it when the collector returns null.

The string fields are checked for presence only, except where a rule is listed. Unknown fields are dropped silently. A failed rule answers a validation error naming the field.

Device data

deviceData carries signals about the cardholder's browser for your payment processor, where they improve authorization rates and reduce 3-D Secure friction.

{
  "deviceData": {
    "device": {
      "ip": "203.0.113.42",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) …",
      "location": { "lat": 37.7749, "lon": -122.4194 }
    }
  }
}
PathTypeRequired when deviceData is presentRules
deviceobjectyes
device.ipstringyesA well-formed IPv4 or IPv6 address, at most 45 characters. It must be the cardholder's address as seen from their browser, not your server's.
device.userAgentstringyesThe browser's User-Agent, non-empty, at most 1024 characters.
device.locationobjectno{ "lat": number, "lon": number } in decimal degrees: lat from −90 to 90, lon from −180 to 180.
device.deviceFingerprintstringnoReserved for integrations agreed with High Wire; most integrations never set it. Letters, digits, _ and -, at most 128 characters.
  • Collect the values in the browser with the Payment SDK's device-screening collector. Any extra keys it adds are dropped by the gateway. An embedded Payment SDK checkout sends no deviceData: HighWire reads the browser's address and location itself.
  • Omit deviceData entirely when you have nothing to send. Never send an empty object or a hand-built substitute.

Authorize and capture

POST /v1/payments/auth-and-capture

Idempotency-Key: honoured.

curl -X POST https://api-payments-staging.highwirepayments.com/v1/payments/auth-and-capture \
  -u "YOUR_API_KEY:YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0193c2e9-4f9a-7c2f-9c83-1b2b3c4d5e6f" \
  -d '{
    "amount": 7200,
    "cardNumber": "5555555555554444",
    "cardExpiration": "1133",
    "cardCvv": "456",
    "cardholderName": "Jordan Example",
    "cardholderEmail": "jordan@example.com",
    "cardholderPhone": "+14155550101",
    "cardholderBillingAddress": "100 Example Street",
    "cardholderBillingZipcode": "94105",
    "cardholderBillingCity": "Springfield",
    "cardholderBillingState": "CA",
    "riskSessionKey": "01928e1c-2a4d-7c8e-8b1a-1234567890ab"
  }'

201 Created:

{
  "paymentId": "01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b",
  "status": "CAPTURED",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444",
  "cardExpiration": "1133",
  "cardholderName": "Jordan Example"
}
FieldTypeNotes
paymentIdstringPersist it. Every later call about this payment (read, capture, void, refund) uses it.
statusenumCAPTURED: done. PENDING: the processor accepted the charge and is still settling it; poll. REQUIRES_ACTION: the customer must authenticate, see 3-D Secure.
amountintegerCents captured.
cardNumberstringMasked.
cardExpirationstringMMYY.
cardholderNamestringAs sent.
customerId, paymentMethodIdstringOnly when savePaymentMethodForFutureUse was true. See Saved payment methods.
nextActionobjectOnly when status is REQUIRES_ACTION.
StatusBodyWhen
201The response above.Captured, pending or awaiting the customer.
400Validation errorA field failed its rule.
400Charge declineRefused by a limit, by fraud screening or by the issuer. See Declines.
400Plain errorFailed to register customer for saved payment method.: the save flag was set and the customer could not be registered. No payment was created.
401, 403Plain errorInvalid credentials or an inactive account (401); a refund-only account (403).
409, 422Plain errorIdempotency key in flight or reused with a different body.
429VELOCITY_LIMITA velocity check refused the charge.

Authorize now, capture later

POST /v1/payments/auth

Idempotency-Key: honoured. Same request body and the same responses as above; a success settles to AUTHORIZED instead of CAPTURED:

{
  "paymentId": "01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d",
  "status": "AUTHORIZED",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444",
  "cardExpiration": "1133",
  "cardholderName": "Jordan Example"
}

The funds are held on the card. Capture them to settle, or void to release them. An authorization that is neither captured nor voided eventually lapses at the processor and the payment becomes EXPIRED; how long a hold lasts is decided by the card networks, so capture as soon as you can.

Capture

POST /v1/payments/{id}/capture

Idempotency-Key: honoured. No request body. Captures the full authorized amount; partial captures are not supported.

curl -X POST https://api-payments-staging.highwirepayments.com/v1/payments/01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d/capture \
  -u "YOUR_API_KEY:YOUR_SECRET" \
  -H "Idempotency-Key: 0193c2f1-2b7d-7a4e-8f10-6c7d8e9f0a1b"

201 Created:

{
  "paymentId": "01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d",
  "status": "CAPTURED",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444"
}
StatusBodyWhen
201The response above.Captured.
400Plain errorPayment is not in a capturable state; the authorization must be successful., The authorization is no longer an open hold and cannot be captured., This payment has already been captured., This payment is being voided and can no longer be captured., or Validation failed (uuid v 7 is expected) for a malformed id.
400Void and capture declineThe processor refused the capture.
404Plain errorPayment not found: <id>: unknown, another account's, or a payment with no authorization to capture. A payment made with auth-and-capture answers 404 here, since it was captured on creation.
401, 403, 409, 422Plain errorAs for a charge. Refund-only accounts cannot capture.
500Plain errorThe capture was accepted but could not be finalized yet: do not retry; poll the payment.

Void

POST /v1/payments/{id}/void

Idempotency-Key: ignored. No request body. Cancels, for its full amount, a payment that has not been captured: an uncaptured AUTHORIZED hold, a charge still PENDING at the processor, or a REQUIRES_ACTION payment whose customer abandoned the 3-D Secure challenge. A captured payment cannot be voided; refund it instead. Accounts limited to refunds and voids may call this route.

curl -X POST https://api-payments-staging.highwirepayments.com/v1/payments/01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d/void \
  -u "YOUR_API_KEY:YOUR_SECRET"

201 Created:

{
  "paymentId": "01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d",
  "status": "VOIDED",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444"
}

cardNumber is null for payments that carry no card number, such as wallet payments made through a checkout.

StatusBodyWhen
201The response above.Voided.
400Plain errorPayment is not in a voidable state; only an in-flight or authorized (uncaptured) payment can be voided. Try to refund the payment instead., The payment can no longer be voided. Try to refund the payment instead., Payment has already been captured. Try to refund the payment instead., Payment has already been voided., A void is already in progress for this payment, or Validation failed (uuid v 7 is expected).
400Void and capture declineThe processor refused the void.
404Plain errorPayment not found: <id>: unknown, another account's, or a payment that never reached the processor (for example a hosted checkout nobody has opened).
401Plain errorInvalid credentials, or an inactive account.
500Plain errorThe void was accepted but could not be finalized yet: do not retry; poll the payment.

3-D Secure

3-D Secure surfacing is enabled per account by High Wire, together with your 3-D Secure return page (an HTTPS page on your site the customer lands on after the challenge). Until it is enabled, a charge whose issuer demands a challenge is declined with declinedReason: "Additional authentication required". Enable it once your integration renders nextAction and polls for the outcome.

When it is enabled and the issuer requires the customer to authenticate, the charge still answers 201, with status: "REQUIRES_ACTION" and a nextAction instead of a settled outcome. Nothing has been charged yet; the payment waits for the customer.

{
  "paymentId": "01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b",
  "status": "REQUIRES_ACTION",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444",
  "cardExpiration": "1133",
  "cardholderName": "Jordan Example",
  "nextAction": {
    "kind": "REDIRECT",
    "url": "https://verify.example.com/3ds/s/7Qx2LwP7aZ0cT5uYbR1d",
    "method": "GET"
  }
}
FieldNotes
kindREDIRECT: open url with a GET, in an iframe or by navigating the page. REDIRECT_FORM: post data to url as an auto-submitted HTML form.
urlThe challenge URL. Opaque and time-limited: do not parse, rewrite or store it beyond the pending window.
methodGET for REDIRECT, POST for REDIRECT_FORM.
dataREDIRECT_FORM only: the form fields to post.
fallbackUrlSometimes present: an alternate URL when the primary one cannot be rendered.

Integration flow:

  1. Render the challenge, typically an iframe whose src is nextAction.url.
  2. The customer completes or fails it. The frame is then sent to your configured return page with a few query parameters describing the outcome. Treat them as a hint for your UI only, never as the settled outcome: anyone can open that page.
  3. Poll GET /v1/payments/{id}. It keeps serving the pending nextAction until the payment settles to CAPTURED or AUTHORIZED on success, DECLINED when authentication fails (the transaction's declinedReason is Authentication failed), or EXPIRED when the challenge times out unanswered.

Notes:

  • A replay under the same Idempotency-Key returns the same response, including the same challenge URL.
  • An abandoned challenge can be cancelled with void, which settles the payment to VOIDED.
  • When savePaymentMethodForFutureUse was true, the response carries customerId and paymentMethodId, but the method is a pending save: it becomes chargeable only once the payment settles successfully, and it is invalidated if the authentication fails, expires or is voided. See Saved payment methods.
  • charge-saved-method never returns REQUIRES_ACTION; an issuer that insists on a challenge there declines the charge.

Declines

A decline is a 400 with the charge decline body. The payment is stored with status: DECLINED for your reporting. The response is stored under the Idempotency-Key, so a new attempt is a new operation with a new key.

declinedReasonSourceWhat to do
Card declined, Insufficient funds, Card expired, Incorrect card details, Card not supported, Duplicate transaction, Processing error. Try again later., Payment processing is unavailable. Please contact support., Payment declinedThe card's issuer or the processor refused the charge. declinedReason is always one of these fixed strings.Show the shopper a generic decline message, keep declinedReason for your logs, and let them try another card.
Additional authentication requiredThe issuer wants 3-D Secure and it could not be surfaced: 3-D Secure is not enabled on the account, or the route cannot show a challenge (charge-saved-method).Enable 3-D Secure surfacing, or collect the card through the Payment SDK or the hosted checkout, which run the challenge for you.
Amount exceeds per-transaction limit, Amount exceeds daily limit, Amount exceeds monthly limitYour merchant limits, checked before the processor is contacted.Ask High Wire to raise the cap ahead of time.
Transaction flagged as high risk.The charge was refused; no money moved.Handle it like any other decline.
auth-and-capture is not available for your account. Please contact support., auth is not available for your account. Please contact support.Your account cannot use the direct card routes.Use the Payment SDK or the hosted checkout.

A charge the processor accepted is never answered with a decline: it answers 201 with status: "PENDING" and settles shortly after. Do not retry a PENDING payment; poll it.

On this page