Payment methods

List available payment methods, save a card, and run the 3D Secure challenge before checkout.

All payment-method routes live under /v1/payment-methods and require the
consumer HMAC and a traveler bearer token. Raw card
data (PAN, CVV, expiry) is never sent to or accepted by these endpoints — card
capture happens client-side through the payment SDK, which returns a
single-use tokenized nonce.

List available payment methods

GET /v1/payment-methods?vertical=flight&fareId=018f... returns the methods
eligible for a given checkout context. vertical is required
(flight, stay, train, car, travel_request); pass either fareId
(before booking, from the built fare) or bookingId (for a post-booking
payment change):

{
  "cards": [
    { "id": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f", "paymentType": "credit_card", "brand": "visa", "lastFour": "1111", "expiry": "12/28", "isDefault": true, "displayName": "My travel card", "isCorporate": false }
  ],
  "postpay": false,
  "paypal": false,
  "btaAccounts": []
}

An expired or unresolvable fareId returns 410 with code: OFFER_EXPIRED —
refresh the fare build before retrying.

Add a card

Saving a card is a three-step handshake: get a client token, tokenize the
card in the payment SDK on the client, then register the resulting nonce.

1. Get a client token

POST /v1/payment-methods/client-token returns the token the mobile or web
client uses to initialise the payment SDK's client-side tokenization.

{ "token": "eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiIuLi4ifQ==" }

2. Tokenize the card client-side

Pass the token to the payment SDK, collect the card fields in its UI, and let
the SDK return a single-use nonce. This step never touches the API — no
endpoint documented on this page accepts a raw card number.

3. Register the card

POST /v1/payment-methods saves the tokenized card against the traveler.

curl -X POST "$TRAVELTECH_BASE_URL/v1/payment-methods" \
  -H "TravelTech-Consumer: $CONSUMER" \
  -H "TravelTech-Timestamp: $TIMESTAMP" \
  -H "TravelTech-Signature: $SIGNATURE" \
  -H "Authorization: Bearer $TRAVELER_JWT" \
  -H "Content-Type: application/json" \
  -d '{"nonce": "tokencc_bf_...", "name": "My travel card"}'
import httpx

from your_signing_module import sign  # see Authentication for the full algorithm

method, path = "POST", "/v1/payment-methods"
body = b'{"nonce": "tokencc_bf_...", "name": "My travel card"}'
timestamp = str(int(time.time()))

response = httpx.post(
    f"{BASE_URL}{path}",
    content=body,
    headers={
        "TravelTech-Consumer": CONSUMER,
        "TravelTech-Timestamp": timestamp,
        "TravelTech-Signature": sign(method, path, body, timestamp),
        "Authorization": f"Bearer {traveler_jwt}",
        "Content-Type": "application/json",
    },
)
const method = "POST";
const path = "/v1/payment-methods";
const body = JSON.stringify({ nonce: "tokencc_bf_...", name: "My travel card" });
const timestamp = String(Math.floor(Date.now() / 1000));

await fetch(`${BASE_URL}${path}`, {
  method,
  body,
  headers: {
    "TravelTech-Consumer": CONSUMER,
    "TravelTech-Timestamp": timestamp,
    "TravelTech-Signature": sign(method, path, body, timestamp),
    Authorization: `Bearer ${travelerJwt}`,
    "Content-Type": "application/json",
  },
});

billingAddressId / invoiceProfileId (both optional) set the card's default
billing references — see
Billing and invoicing. Response
(201):

{
  "id": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f",
  "paymentType": "credit_card",
  "lastFour": "1111",
  "brand": "visa",
  "expiry": "12/28",
  "isDefault": false,
  "displayName": "My travel card",
  "isCorporate": false,
  "holderName": "Ada Lovelace"
}

An invalid or already-consumed nonce returns 502:

{
  "error": {
    "code": "BAD_GATEWAY",
    "message": "Provider rejected the request.",
    "details": [],
    "recoveryAction": "RETRY_NOW"
  }
}

3D Secure challenge

Cards that require Strong Customer Authentication go through a lookup-then-verify
handshake instead of the direct registration above.

1. Lookup

POST /v1/payment-methods/3ds-lookup takes the tokenized nonce and the
device-fingerprint reference the payment SDK's 3D Secure prepare step
returned, and runs the lookup:

{ "nonce": "tokencc_bf_...", "dfReferenceId": "df-ref-018f..." }

Response (ThreeDSecureLookupOut) — branch on threeDSecureInfo, not on
whether lookup.acsUrl is set:

{
  "lookup": { "acsUrl": "https://acs.example.com/challenge", "pareq": "eJzT...", "transactionId": "txn-018f...", "threeDSecureVersion": "2.2.0" },
  "paymentMethod": { "nonce": "tokencc_bf_verified_..." },
  "threeDSecureInfo": { "liabilityShifted": false, "liabilityShiftPossible": true }
}
liabilityShiftedliabilityShiftPossibleClient action
trueanyFrictionless — skip the challenge; call POST /v1/payment-methods/verify with paymentMethod.nonce.
falsetrueChallenge required — hand lookup to the SDK; call verify with the nonce it resolves, only if it then reports liabilityShifted.
falsefalseThe issuer could not verify the card — stop, do not call verify; ask the traveler for a different payment method.

An unmappable or incomplete provider response (for example a challenge
required with no acsUrl to present it at) returns 502 with
code: BAD_GATEWAY.

2. Verify

POST /v1/payment-methods/verify registers the card once liability has
shifted. The verification amount is fixed server-side, never caller-supplied:

{
  "nonce": "tokencc_bf_verified_...",
  "name": "My travel card",
  "currency": "EUR",
  "numberAlias": "alias-number-018f...",
  "expiryMonthAlias": "alias-month-018f...",
  "expiryYearAlias": "alias-year-018f..."
}

The response is the same PaymentMethodOut shape shown above.

See the API Reference for the complete request and
response schemas.

Reporting issues

This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.


Did this page help you?