Skip to main content
Use this guide to implement a production wallet flow where the frontend handles wallet UX and your backend performs Core API calls. The core contract is simple: capture encrypted payload.card_data in onCapture and forward it unchanged to your server.

Flow Overview

  1. Initialize SDK in the frontend
  2. Create a transaction
  3. Mount wallet buttons
  4. On capture, send encrypted data to backend
  5. Backend creates transaction in Rinne API
Forward payload.card_data as encrypted secure data to your backend. Avoid logging sensitive payload fields in plaintext logs.

Frontend Implementation

checkout.ts
import { Rinne } from '@rinnebr/js'

const rinne = new Rinne({
  merchantId: 'your-merchant-id',
  environment: 'production'
})

const transaction = await rinne.transaction.create({
  amount: 3990,
  currency: 'BRL',
  country: 'BR',
  lineItems: [{ label: 'Order Total', amount: 3990 }]
})

const handlers = {
  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,
          requestId: crypto.randomUUID()
        })
      })

      if (!response.ok) {
        const body = await response.json()
        throw new Error(body.message ?? 'Wallet payment failed')
      }
    } catch (error) {
      fail({ message: error instanceof Error ? error.message : 'Wallet payment failed' })
    }
  },
  onError: (error) => console.error('Wallet error:', error),
  onCancel: () => console.log('Wallet cancelled')
}

const applePay = await rinne.elements.applePay(transaction, {
  button: { type: 'pay', color: 'black', locale: 'pt' },
  ...handlers
})
await applePay.mount('#apple-pay')

const googlePay = await rinne.elements.googlePay(transaction, {
  button: { type: 'pay', color: 'black', locale: 'pt' },
  colorScheme: 'dark',
  ...handlers
})
await googlePay.mount('#google-pay')

Backend Example (Node.js)

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

  try {
    const rinneResponse = await fetch(
      `https://api.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
        },
        body: JSON.stringify({
          amount,
          currency,
          provider: 'RINNE',
          payment_method: paymentMethod,
          capture_method: 'ECOMMERCE',
          request_id: requestId,
          card_data: cardData,
          installments: 1
        })
      }
    )

    const body = await rinneResponse.json()

    if (!rinneResponse.ok) {
      return res.status(rinneResponse.status).json({ message: body.message ?? 'Charge failed' })
    }

    return res.status(200).json(body)
  } catch (error) {
    return res.status(500).json({ message: 'Internal wallet processing error' })
  }
})
Never call Rinne transaction APIs directly from the browser. Keep your API key server-side.

Verification Checklist

Wallet checkout is correctly implemented when:
  • onCapture sends encrypted payloads to your backend
  • Backend creates transaction with your API key
  • fail() is called whenever backend processing fails
  • If transaction status is AWAITING_3DS, your checkout continues with the 3DS flow