# Stripe PaymentIntent in Vue/Nuxt

Vue apps do not need a Stripe wrapper component. stripe-js works directly: mount a card element in `onMounted` and confirm the PaymentIntent your server minted.

## The component

```
[script setup]
import { ref, onMounted } from 'vue';
import { loadStripe } from '@stripe/stripe-js';

const cardEl = ref(null);
let stripe, card;

onMounted(async () => {
  stripe = await loadStripe('YOUR_PUBLISHABLE_KEY_VALUE');
  const elements = stripe.elements();
  card = elements.create('card');
  card.mount(cardEl.value);
});

async function pay() {
  // fetch the client secret from your server first
  const { clientSecret } = await fetch('/api/payment-intent', {
    method: 'POST',
    body: JSON.stringify({ amount: 2000, currency: 'usd' }),
  }).then((r) => r.json());

  const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
    payment_method: { card },
  });
  if (error) {
    console.log(error.message);
  } else if (paymentIntent.status === 'succeeded') {
    console.log('paid');
  }
}
[/script]

[template]
  [div]
    [div ref="cardEl"][/div]
    [button @click="pay"]Pay[/button]
  [/div]
[/template]
```

## Nuxt specifics

- Put the publishable key in `runtimeConfig.public` in `nuxt.config.ts` and read it with `useRuntimeConfig()`, never hardcode it in the component.
- The `/api/payment-intent` server route creates the intent with the secret key from private runtime config and returns only the client secret.

## Notes

- Mount the card element after `loadStripe` resolves; the element needs the DOM node to exist.
- `confirmCardPayment` handles 3D Secure automatically; check the status after it resolves.