# Add or verify card in member's wallet

`POST /api/members/{memberId}/wallet/card`

- **Success:** `201 Created`
- **Products:** Operator (`FOH`), Consumer (`CONSUMER`) (any one of these products, not all)
- **Features:** Online Bookings (`ONLINEBOOK`), Online Joining (`ONLINEJOIN`), Front of House Generic Access (`FOHGENERIC`) (any one of these features, not all)

HTML version: [Add or verify card in member's wallet](https://support.gladstonesoftware.cloud/reference/members/add-or-verify-card-in-members-wallet)

## Overview

Registers a card against a member so it can be charged later without the member re-entering it. It does this by running a zero-amount `VERIFY` transaction with the card processor behind GladstonePay (CardStream), which stores the card in the member's wallet. The card itself never reaches this endpoint: the request body carries a `paymentToken` and nothing else that identifies the card — there is no field for a card number, expiry date or security code.

The call has two legs. On the first leg you send the payment token and the device details. If the processor is satisfied, the card is stored and the call is finished. If the processor demands a 3D Secure challenge instead, you complete that challenge with the member in the browser and then call this same endpoint a second time with `threeDSecureChallengeReference` and `threeDSecureChallengeResponse`. The presence of `threeDSecureChallengeResponse` is what marks the request as a continuation rather than a fresh attempt.

The wallet is created on demand. If the member has no wallet yet, one is created when the transaction succeeds, and subsequent calls reuse it. You never create a wallet yourself.

Sending a card the member already holds is not an error and does not add a second entry for it: the processor recognises the card already in the wallet and treats the request as a repeat use of that card rather than a new one. That is precisely why `saveRcpCard` clears the wallet first — the first payment of a recurring agreement must not be recorded as a repeat.

Reach for this endpoint when a member is adding a card for future use — saving a card during checkout, or setting up the card that recurring charges such as membership renewals will be taken from. That second case is what `saveRcpCard` is for, and it is destructive: it deletes the member's existing wallet before running the transaction, so do not set it on a routine "save this card" flow.

Authorisation is checked against the member in your access token. The `memberId` in the path must be that member, or a member linked to them; anything else is rejected with 403 before anything else happens.

## Endpoint

`POST {base_url}/api/members/{memberId}/wallet/card`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `memberId` | integer (int32) | Yes | The member whose wallet the card is stored against. Must be the member in your access token, or a member linked to them, otherwise the request is rejected with 403. If no member holds this id the call fails with 404 before the card processor is contacted. |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `paymentToken` | string | Yes | The token that stands in for the card the member has just entered. It is passed unchanged to the card processor and is the only representation of the card the request carries. Required on every call, but ignored on the 3D Secure continuation leg, which sends only the challenge reference and response. |
| `threeDSecureChallengeReference` | string | No | Identifies the 3D Secure challenge you are answering. Omit it on the first leg; on the continuation leg send back the reference that came with the challenge details in the first leg's response. Send it together with `threeDSecureChallengeResponse` — the two are passed to the processor as a pair, along with the member's wallet id if they already have one. |
| `threeDSecureChallengeResponse` | string | No | The result the member's browser posted back after completing the 3D Secure challenge, as a URL-encoded query string. Setting it switches the request into continuation mode: it is URL-decoded and passed to the processor as its individual 3D Secure response fields, together with `threeDSecureChallengeReference` and nothing else, so `paymentToken` and `deviceInformation` are not forwarded on this leg. `saveRcpCard` still takes effect. Omit it on the first leg. |
| `deviceInformation` | object | Yes | Device fingerprint used by the processor's 3D Secure checks. The object is required, but every property inside it is optional and each is left out of the processor request when empty, so an empty object is accepted. It is supplemented with the `X-Forwarded-For`, `User-Agent`, `Accept`, `Accept-Encoding` and `Accept-Language` headers of your request — forward the member's original browser headers rather than your own server's, or the fingerprint will describe your server. The member's IP address is only passed to the acquiring bank when it is IPv4. Not forwarded on the 3D Secure continuation leg. |
| `deviceInformation.timeZone` | string | No | The member's device time zone, forwarded unchanged as the device time zone in the 3D Secure fingerprint. Left out of the processor request entirely when empty. It is not validated or transformed. |
| `deviceInformation.capabilities` | string | No | The device capability string, forwarded unchanged as the device capabilities value in the 3D Secure fingerprint. Left out of the processor request entirely when empty. It is not validated or transformed. |
| `deviceInformation.screenResolution` | string | No | The device screen resolution as `WIDTHxHEIGHTxCOLOURDEPTH`, for example `1920x1080x24`. The third part is checked: if the colour depth is above zero but not one of 1, 4, 8, 15, 16, 24, 32 or 48, it is rounded down to the nearest of those before the value is sent. Anything that is not three `x`-separated parts is passed through untouched. Left out of the processor request entirely when empty. |
| `saveRcpCard` | boolean | No | Set this to `true` to make the card being added the member's default card for recurring card payments — the card the platform charges automatically for things like membership renewals. Defaults to `false`. It is only accepted when the `VERIFYRCPCARD` feature is enabled for your integration — as indicated by the `Features` header on the request — and the call fails with 400, changing nothing, when it is not. When it is `true` and the member already has a wallet, that wallet is first deleted at the card processor, along with the member's link to it, so the processor treats this as the first payment of a new recurring agreement rather than a repeat of an existing one — this is not reversible, so do not set it speculatively. If the transaction succeeds but the processor returns no cross-reference for the card, the call fails with 422 after the new wallet has already been recorded, and no recurring card is set. |

## Sample request

```bash title="cURL"
curl -X POST '{base_url}/api/members/{memberId}/wallet/card' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "paymentToken": "string",
  "threeDSecureChallengeReference": "string",
  "threeDSecureChallengeResponse": "string",
  "deviceInformation": {
    "timeZone": "string",
    "capabilities": "string",
    "screenResolution": "string"
  },
  "saveRcpCard": true
}'
```

## Sample response

```json title="201 Created"
1
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `saveRcpCard` was `true` but the `Features` header on the request does not include `VERIFYRCPCARD`: `SaveRcpCard is set to true in request. This is only a valid request if the VERIFYRCPCARD feature is enabled.` Also returned when `paymentToken` or `deviceInformation` is missing from the body, and when the card processor rejects the card — the processor's numeric response code is returned as the message. |
| `404` | Not found | No member exists with the `memberId` in the path: `Member with ID {memberId} does not exist`. Checked before anything is sent to the card processor. |
| `422` | Unprocessable entity | The card processor rejected `paymentToken` as invalid, or `saveRcpCard` was `true` and the processor's successful response carried no cross-reference for the card: `xref is missing for rcp card`. In the second case the card has been added and the wallet recorded, but no recurring card has been set. |
| `500` | Internal server error | Any unexpected server-side failure, including being unable to reach the card processor and, on a `saveRcpCard` request, a failure to delete the member's existing wallet at the processor. The response has no body. |
