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

# Wallet transactions

> Process Apple Pay and Google Pay transactions through your backend using rinne-js payloads.

Wallet transactions use the same transaction endpoint as card payments, but `card_data` is tokenized and captured by wallet elements.

<Info>
  Wallet and 3DS are complementary. If a wallet transaction returns `AWAITING_3DS`, continue with either official 3DS flow (session-first or transaction-first), based on your organization architecture.
</Info>

## Architecture

1. Frontend creates a wallet transaction object with rinne-js.
2. Frontend mounts Apple Pay and Google Pay elements.
3. `onCapture` returns encrypted `payload.card_data` and `payload.payment_method`.
4. Frontend sends payload data to your backend.
5. Backend calls transaction create endpoint in self or merchant context.

<Warning>
  Do not call the Core API directly from the browser. Keep `x-api-key` on your backend only.
</Warning>

<Warning>
  Treat `payload.card_data` as a secure encrypted payload. Forward it to your backend without decrypting, reformatting, or storing sensitive values in logs.
</Warning>

<Note>
  `network_token` and `cryptogram` are accepted only as encrypted values (`ev:` prefix), exactly as the wallet elements emit them. Plaintext values are rejected with `400 VALIDATION_ERROR`.
</Note>

## Transaction endpoint context

| Context                            | Endpoint                                       |
| ---------------------------------- | ---------------------------------------------- |
| Self (company)                     | `POST /v1/transactions`                        |
| Organization on behalf of merchant | `POST /v1/merchants/{merchantId}/transactions` |

## Map rinne-js payload to API fields

| rinne-js payload                       | Rinne Core API field | Notes                                 |
| -------------------------------------- | -------------------- | ------------------------------------- |
| `payload.card_data`                    | `card_data`          | Send as-is from frontend to backend   |
| `payload.payment_method`               | `payment_method`     | Usually `CREDIT_CARD` or `DEBIT_CARD` |
| `payload.transaction.details.amount`   | `amount`             | Amount in cents                       |
| `payload.transaction.details.currency` | `currency`           | Usually `BRL`                         |
| Backend-generated UUID                 | `request_id`         | Idempotency key                       |

## Backend transaction request example

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST 'https://api-sandbox.rinne.com.br/core/v1/merchants/MERCHANT_ID/transactions' \
    -H 'x-api-key: YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "provider": "RINNE",
      "request_id": "wallet-order-0001",
      "amount": 3990,
      "currency": "BRL",
      "capture_method": "ECOMMERCE",
      "payment_method": "CREDIT_CARD",
      "installments": 1,
      "card_data": {
        "network_token": "ev:encrypted:network_token",
        "cryptogram": "ev:encrypted:cryptogram",
        "expiry_month": "12",
        "expiry_year": "2028",
        "cardholder_name": "Maria Santos",
        "last_digits": "1111",
        "wallet_type": "APPLE_PAY",
        "display_name": "Visa 1111"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "tx_123456789",
    "status": "PROCESSING",
    "payment_method": "CREDIT_CARD",
    "amount": 3990,
    "currency": "BRL"
  }
  ```
</ResponseExample>

## Backend handler example (Node.js)

```typescript server.ts theme={null}
app.post('/api/checkout/wallet', async (req, res) => {
  const { cardData, paymentMethod, amount, currency } = req.body

  try {
    const response = await fetch(
      `https://api-sandbox.rinne.com.br/core/v1/merchants/${process.env.RINNE_MERCHANT_ID}/transactions`,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': process.env.RINNE_API_KEY as string
        },
        body: JSON.stringify({
          provider: 'RINNE',
          request_id: crypto.randomUUID(),
          amount,
          currency,
          capture_method: 'ECOMMERCE',
          payment_method: paymentMethod,
          installments: 1,
          card_data: cardData
        })
      }
    )

    const body = await response.json()

    if (!response.ok) {
      return res.status(response.status).json(body)
    }

    return res.status(200).json(body)
  } catch {
    return res.status(500).json({ message: 'Wallet processing failed' })
  }
})
```

## Frontend capture handling

In `onCapture`, always call `fail()` when backend processing fails so wallet UI can recover.

```typescript theme={null}
const walletHandlers = {
  onCapture: async (payload, fail) => {
    try {
      const response = await fetch('/api/checkout/wallet', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          cardData: payload.card_data,
          paymentMethod: payload.payment_method,
          amount: payload.transaction.details.amount,
          currency: payload.transaction.details.currency
        })
      })

      if (!response.ok) {
        const body = await response.json()
        throw new Error(body?.error?.message ?? 'Wallet authorization failed')
      }
    } catch (error) {
      fail({
        message: error instanceof Error ? error.message : 'Wallet authorization failed'
      })
    }
  }
}
```

## 3DS behavior in wallet flows

Most wallet payloads already contain tokenized authentication data (`network_token` + `cryptogram`).

If the transaction still returns `AWAITING_3DS`, run your regular 3DS flow:

1. Create a 3DS session.
2. Complete challenge in frontend with `tds_session_id`.
3. Call authenticate endpoint in your context:
   * Self: `/v1/transactions/{transactionId}/authenticate`
   * Merchant: `/v1/merchants/{merchantId}/transactions/{transactionId}/authenticate`

You can also run session-first 3DS before transaction creation if that better matches your checkout orchestration.

### Optional policy flags for wallet card transactions

The same card transaction flags are available in wallet-backed card requests:

* `require_3ds: true`: use this when you want wallet transactions to deterministically enter `AWAITING_3DS` in your transaction-first flow.
* `refuse_on_challenge: true`: use this when you want challenge-triggered wallet transactions to fail fast as `REFUSED` (`status_reason = CHALLENGE_NOT_ALLOWED`) instead of requiring challenge handling.

<Warning>
  `require_3ds` and `refuse_on_challenge` cannot both be `true` in the same transaction request.
</Warning>

## Production checklist

<Check>
  Wallet checkout is ready for production when:

  * You send wallet `card_data` only to your backend.
  * You create transactions with idempotent `request_id` values.
  * You call `fail()` in `onCapture` for all backend failures.
  * You handle `PROCESSING`, `APPROVED`, `REFUSED`, and `AWAITING_3DS` outcomes.
  * You use webhooks for final transaction state updates.
</Check>

## Related docs

* [Wallet Elements (rinne-js)](/rinne-js/wallet-elements)
* [Guide: Wallet Checkout (rinne-js)](/rinne-js/guides/wallet-checkout)
* [3D Secure authentication](/guides/three-d-secure-authentication)
