Skip to main content
Wallet transactions use the same transaction endpoint as card payments, but card_data is tokenized and captured by wallet elements.
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.

Architecture

  1. Frontend creates a wallet transaction object with RinneJS.
  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.
Do not call the Core API directly from the browser. Keep x-api-key on your backend only.
Treat payload.card_data as a secure encrypted payload. Forward it to your backend without decrypting, reformatting, or storing sensitive values in logs.

Transaction endpoint context

ContextEndpoint
Self (company)POST /v1/transactions
Organization on behalf of merchantPOST /v1/merchants/{merchantId}/transactions

Map RinneJS payload to API fields

RinneJS payloadRinne Core API fieldNotes
payload.card_datacard_dataSend as-is from frontend to backend
payload.payment_methodpayment_methodUsually CREDIT_CARD or DEBIT_CARD
payload.transaction.details.amountamountAmount in cents
payload.transaction.details.currencycurrencyUsually BRL
Backend-generated UUIDrequest_idIdempotency key

Backend transaction request example

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": "AAABBBCCC123",
      "expiry_month": "12",
      "expiry_year": "2028",
      "wallet_type": "APPLE_PAY",
      "display_name": "Visa 1111"
    }
  }'
{
  "id": "tx_123456789",
  "status": "PROCESSING",
  "payment_method": "CREDIT_CARD",
  "amount": 3990,
  "currency": "BRL"
}

Backend handler example (Node.js)

server.ts
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.
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.
require_3ds and refuse_on_challenge cannot both be true in the same transaction request.

Production checklist

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.