> ## Documentation Index
> Fetch the complete documentation index at: https://docs.imprint.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Add to Wallet

> Let your customers add their Imprint card to Apple Pay or Google Pay from inside your own app

<Info>
  Apple Pay provisioning is live. Google Pay is documented below but not yet accepted by the
  endpoint. Reach out to your Imprint team to coordinate wallet enablement for your program.
</Info>

## Overview

Add to Wallet lets a customer provision their Imprint card into a device wallet — Apple Pay or
Google Pay — **without leaving your app**.

The **platform wallet SDK** is the wallet framework the operating system provides: PassKit
(`PKAddPaymentPassViewController`) on iOS, and the TapAndPay client on Android. Apple and Google
ship it, not Imprint, and your app links it to show the native *Add to Wallet* sheet.

Your app collects a short-lived handshake from that SDK, passes it to Imprint, and Imprint returns
the encrypted payload the SDK needs to finish adding the card.

Imprint never asks you to handle the card number. The provisioning payload is encrypted for the
wallet provider, so your app acts purely as a courier between the platform wallet SDK and Imprint.

**What this unlocks:** a customer who has just been approved for a card can tap *Add to Apple
Wallet* in your app and start making contactless payments immediately — before the physical card
arrives in the mail.

### What you need

| Requirement                                          | Notes                                                                                                                                                                                                                                                             |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A **native iOS or Android app**                      | This flow is only supported for mobile applications. See [Not supported](#not-supported) for web.                                                                                                                                                                 |
| **Wallet enablement for your program**               | Reach out to your Imprint team to coordinate wallet enablement for your program. This is not self-serve — provisioning calls fail until your program is enabled.                                                                                                  |
| Wallet entitlements from Apple or Google             | Apple requires the `com.apple.developer.payment-pass-provisioning` entitlement; Google requires allowlisting for the TapAndPay push-provisioning API. Both are granted by the platform, not by Imprint.                                                           |
| An API key with card write access                    | Contact your Imprint team if provisioning calls return `403`.                                                                                                                                                                                                     |
| The `payment_method_id` of a card in `ACTIVE` status | Retrieved from [Retrieve a payment method](https://docs.imprint.co/api-reference/payment-methods/retrieve-a-payment-method) or the [Payment Method Event Notification](https://docs.imprint.co/api-reference/webhooks/payment-method-event-notification) webhook. |

## How the flow works

The critical detail: **your app talks to the platform wallet SDK first.** The wallet SDK produces
the values Imprint needs. Only then do you call Imprint.

<Steps>
  <Step title="Your app asks the platform wallet to begin">
    The customer taps *Add to Wallet*. Your app starts the platform's add-card flow
    (`PKAddPaymentPassViewController` on iOS, `TapAndPay` on Android).
  </Step>

  <Step title="The wallet SDK hands your app the values Imprint needs">
    On iOS this is a certificate chain, a nonce, and a nonce signature — a single-use handshake.
    On Android it is the active wallet ID and a stable device ID, which do not expire.
  </Step>

  <Step title="Your app calls Imprint">
    `POST /v2/payment_methods/{payment_method_id}/provision/{wallet}`, forwarding the handshake.
  </Step>

  <Step title="Imprint returns an encrypted provisioning payload">
    Imprint validates that the payment method belongs to you, then requests the encrypted payload
    from the card network on your behalf.
  </Step>

  <Step title="Your app hands the payload back to the wallet SDK">
    The SDK decrypts it and adds the card. The customer sees the card appear in their wallet.
  </Step>
</Steps>

<Warning>
  **Apple Pay only:** the handshake from step 2 is single-use and short-lived. You cannot cache an
  Imprint provisioning response and replay it later, and you cannot call Imprint with a nonce from
  a previous attempt. If provisioning fails, dismiss and restart
  `PKAddPaymentPassViewController` from step 1 so PassKit issues a fresh nonce.

  Google Pay works differently — `wallet_id` and `device_id` are stable identifiers, not a
  one-time handshake, so the same values can be re-sent on a retry.
</Warning>

In both cases the **provisioning payload Imprint returns** is single-use: hand it straight to the
platform wallet SDK and never cache or persist it.

## The endpoint

```
POST /v2/payment_methods/{payment_method_id}/provision/{wallet}
```

| Parameter           | In   | Description                                                           |
| ------------------- | ---- | --------------------------------------------------------------------- |
| `payment_method_id` | path | The payment method to provision.                                      |
| `wallet`            | path | The target device wallet. `apple_pay` today; `google_pay` is planned. |

The wallet is named explicitly in the path so the contract is unambiguous about which wallet you
are provisioning, and so additional wallets can be added later without changing this one.

See the [API reference](https://docs.imprint.co/api-reference/payment-methods/add-a-payment-method-to-a-device-wallet)
for the full request and response schemas.

<Note>
  This endpoint is the same for every Imprint program. There is no partner-specific path — your
  API key determines which program and which cards you can act on.
</Note>

## Apple Pay

### Request

Take these three values from your `PKAddPaymentPassViewControllerDelegate` callback:

```
addPaymentPassViewController:generateRequestWithCertificateChain:nonce:nonceSignature:
```

PassKit gives you `certificateChain` as `[Data]` and `nonce` / `nonceSignature` as `Data`.
**Base64-encode each one before sending it** — Imprint rejects raw bytes with
`invalid_request`. Do not otherwise transform the values.

```swift theme={null}
let body: [String: Any] = [
    "certificates": certificateChain.map { $0.base64EncodedString() },
    "nonce": nonce.base64EncodedString(),
    "nonce_signature": nonceSignature.base64EncodedString(),
]
```

```json theme={null}
{
  "certificates": ["MIIEg...leaf", "MIIDx...intermediate"],
  "nonce": "3vT9Kw==",
  "nonce_signature": "R0hJSkt..."
}
```

| Field             | Type       | Description                                                                                                        |
| ----------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
| `certificates`    | `string[]` | Base64-encoded. The leaf certificate followed by the intermediate certificate, in the order PassKit supplied them. |
| `nonce`           | `string`   | Base64-encoded nonce from PassKit.                                                                                 |
| `nonce_signature` | `string`   | Base64-encoded nonce signature from PassKit.                                                                       |

### Response

```json theme={null}
{
  "wallet": "apple_pay",
  "provisioning_payload": {
    "activation_data": "YWN0aXZhdGlvbg==",
    "encrypted_pass_data": "ZW5jcnlwdGVkUGFzcw==",
    "ephemeral_public_key": "ZXBoZW1lcmFsS2V5"
  }
}
```

Map the payload onto `PKAddPaymentPassRequest`. **Base64-decode each value first** — Imprint
returns Base64 strings, but the PassKit properties are `Data`. Assigning the strings without
decoding hands PassKit the wrong bytes and provisioning fails.

| Imprint field          | `PKAddPaymentPassRequest` property |
| ---------------------- | ---------------------------------- |
| `activation_data`      | `activationData`                   |
| `encrypted_pass_data`  | `encryptedPassData`                |
| `ephemeral_public_key` | `ephemeralPublicKey`               |

```swift theme={null}
let request = PKAddPaymentPassRequest()
request.activationData = Data(base64Encoded: response.activationData)
request.encryptedPassData = Data(base64Encoded: response.encryptedPassData)
request.ephemeralPublicKey = Data(base64Encoded: response.ephemeralPublicKey)
```

Then pass the request to the completion handler PassKit gave you in the delegate callback.

<Note>
  Imprint uses elliptic-curve key exchange, so `ephemeral_public_key` is always the key field in
  the response. You do not need to handle the RSA `wrappedKey` variant.
</Note>

## Google Pay

<Info>
  Google Pay is **not yet accepted** by [the endpoint](#the-endpoint) — `wallet: google_pay`
  currently returns `400 wallet_unsupported`. The shape below is published so you can plan your
  Android integration; it will be enabled without a breaking change to the Apple Pay contract.
</Info>

### Request

```json theme={null}
{
  "wallet_id": "3a5f9c21e8b74d6f",
  "device_id": "9b1c7e04f2a8"
}
```

| Field       | Type     | Description                                                           |
| ----------- | -------- | --------------------------------------------------------------------- |
| `wallet_id` | `string` | The active wallet ID from the TapAndPay client.                       |
| `device_id` | `string` | A stable hardware-scoped device identifier from the TapAndPay client. |

### Response

```json theme={null}
{
  "wallet": "google_pay",
  "provisioning_payload": {
    "opaque_payment_card": "eyJraWQiOi...",
    "network": "VISA",
    "wallet_type": "GOOGLE_PAY"
  }
}
```

| Field                 | Type     | Description                                                                                          |
| --------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `opaque_payment_card` | `string` | The encrypted payload for TapAndPay. Pass through unmodified.                                        |
| `network`             | `string` | The card network. **May be absent** depending on the issuing processor for your program — see below. |
| `wallet_type`         | `string` | Echoes the wallet you provisioned. Always `GOOGLE_PAY`.                                              |

`opaque_payment_card` is the only value that must come from this response. Build the rest of the
`PushTokenizeRequest` from values you already have:

| `PushTokenizeRequest` setter | Where the value comes from                                               |
| ---------------------------- | ------------------------------------------------------------------------ |
| `setOpaquePaymentCard`       | `opaque_payment_card` from this response                                 |
| `setNetwork`                 | The card network, mapped to a TapAndPay `CardNetwork` constant           |
| `setTokenServiceProvider`    | The network's token service provider — **not returned by this endpoint** |
| `setDisplayName`             | Your own copy, or the cardholder name                                    |
| `setLastDigits`              | The payment method object                                                |

<Warning>
  `wallet_type` is **not** a `TokenServiceProvider`. TapAndPay requires
  `setTokenServiceProvider` on every request, and its value is derived from the card network
  (for example a Visa card uses `TOKEN_PROVIDER_VISA`), not from the wallet you are provisioning
  into. Passing `"GOOGLE_PAY"` there will fail.

  `network` is also populated by the issuing processor and is not guaranteed to be present for
  every program. Because your program's network is fixed and known ahead of time, treat both
  `network` and the token service provider as program configuration rather than reading them off
  each response. Confirm the correct constants with your Imprint team during integration.
</Warning>

## Errors

Provisioning errors use the standard Imprint error envelope described in
[Error handling](/error-handling), with a machine-readable `code`:

```json 422 theme={null}
{
  "error": {
    "code": "device_ineligible",
    "message": "this device cannot accept this card"
  }
}
```

| Status | `code`                     | What to do                                                                                                                                                             |
| ------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `invalid_request`          | A required field is missing or not valid Base64, or the handshake was rejected — including a stale PassKit nonce. Get a fresh handshake and retry; do not retry as-is. |
| `400`  | `wallet_unsupported`       | The `wallet` path value is not a wallet Imprint supports yet.                                                                                                          |
| `403`  | `permission_denied`        | Your API key lacks the `CARD_WRITE` scope. Contact your Imprint team.                                                                                                  |
| `404`  | `payment_method_not_found` | The payment method does not exist, or does not belong to your program.                                                                                                 |
| `409`  | `already_provisioned`      | This card is already in this wallet on this device. Treat as success in your UI.                                                                                       |
| `422`  | `device_ineligible`        | The device cannot accept this card. Surface a message; retrying will not help.                                                                                         |
| `504`  | —                          | An upstream timeout. The request may or may not have been applied; check the wallet state before retrying, and get a fresh Apple Pay handshake first.                  |

<Warning>
  Do not blind-retry a failed **Apple Pay** call. Each PassKit nonce is single-use, so replaying
  the original request body fails with `invalid_request` — you must go back through
  `PKAddPaymentPassViewController` for a new one.

  Google Pay retries may re-send the same `wallet_id` and `device_id`.
</Warning>

## Not supported

|                                     | Why                                                                                                                                                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Web / browser push provisioning** | Adding a card to a wallet from a web page has materially different request and response shapes. It is deliberately out of scope for this contract rather than bolted on. Talk to your Imprint team if you need it. |
| **Samsung Pay**                     | Not currently supported. The `wallet` path parameter is designed so additional wallets can be added without a breaking change.                                                                                     |

## Security notes

* **Provisioning payloads are encrypted for the wallet provider.** Your app cannot read the card
  number from them, and neither can anyone intercepting them.
* **Payloads are single-use.** Do not log them, cache them, or persist them.
* **Ownership is enforced server-side.** Imprint verifies that the `payment_method_id` belongs to
  the program your API key is scoped to. A payment method from another program returns `404`,
  not `403`, so the endpoint does not reveal whether an unknown ID exists.
* **Keep your API key server-side.** You must call this endpoint through your app's backend
  proxy — your Imprint API key must never ship inside your mobile app binary.
