Vue 3
Wajub Components for Vue 3 and Nuxt — complete examples with server routes, hosted checkout, and payment fields.
18 min read
Install:
npm install @wajub/vue @wajub/jsNuxt 3 — full example
Server route
export default defineEventHandler(async (event) => {
const { cartId } = await readBody(event);
const res = await fetch('https://api.wajub.com/payments', {
method: 'POST',
headers: {
Authorization: process.env.WAJUB_SECRET_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 25000,
currency: 'XAF',
customer: { email: 'buyer@example.com' },
callback: `${process.env.NUXT_PUBLIC_APP_URL}/order/return`,
metadata: { cart_id: cartId },
}),
});
if (!res.ok) throw createError({ statusCode: 502, message: 'Payment init failed' });
const { authorization_token } = await res.json();
return { sessionId: authorization_token };
});Checkout page
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { WajubProvider, CheckoutEmbed } from '@wajub/vue';
const sessionId = ref<string | null>(null);
const error = ref<string | null>(null);
onMounted(async () => {
try {
const data = await $fetch('/api/checkout/session', {
method: 'POST',
body: { cartId: 'cart_123' },
});
sessionId.value = data.sessionId;
} catch {
error.value = 'Could not start checkout';
}
});
</script>
<template>
<ClientOnly>
<p v-if="error">{{ error }}</p>
<p v-else-if="!sessionId">Loading checkout…</p>
<WajubProvider v-else>
<CheckoutEmbed
:session-id="sessionId"
layout="tabs"
:appearance="{ primaryColor: '#6366f1' }"
@success="() => navigateTo('/order/complete')"
@error="(e) => { error = e.message; }"
/>
</WajubProvider>
</ClientOnly>
</template>Hosted checkout — minimal
<script setup lang="ts">
import { WajubProvider, CheckoutEmbed } from '@wajub/vue';
defineProps<{ sessionId: string }>();
</script>
<template>
<WajubProvider>
<CheckoutEmbed
:session-id="sessionId"
@success="() => (window.location.href = '/thanks')"
@error="(e) => console.error(e)"
/>
</WajubProvider>
</template>CheckoutEmbed props & events
Props mirror EmbeddedConfig:
session-id, locale, layout, appearance, min-height, etc.
Events: @success, @error, @ready, @cancel, @breakdown, @load-error, …
Live appearance updates
CheckoutEmbed remounts when session-id changes. Use
@ready to capture the instance and call update() for live styling.
Payment field components update automatically when appearance props change.
Payment fields
<script setup lang="ts">
import { ref } from 'vue';
import type { ComponentInstance } from '@wajub/js';
import { WajubProvider, CardComponent, useConfirmPayment } from '@wajub/vue';
const props = defineProps<{ sessionId: string }>();
const confirm = useConfirmPayment(props.sessionId);
const cardRef = ref<ComponentInstance | null>(null);
const paying = ref(false);
async function pay() {
if (!cardRef.value) return;
paying.value = true;
try {
await confirm(cardRef.value);
} finally {
paying.value = false;
}
}
</script>
<template>
<WajubProvider>
<CardComponent
:session-id="sessionId"
:appearance="{ primaryColor: '#2563eb', labels: 'floating' }"
@instance="(c) => { cardRef = c; }"
/>
<button :disabled="paying || !cardRef" @click="pay">
{{ paying ? 'Processing…' : 'Pay' }}
</button>
</WajubProvider>
</template>Payment + address
<WajubProvider>
<AddressComponent :session-id="sessionId" address-mode="shipping" />
<PaymentComponent
:session-id="sessionId"
collect-address="shipping"
:collect-name="true"
layout="tabs"
@instance="(c) => { paymentRef = c; }"
/>
</WajubProvider>API reference
| Export | Description |
|---|---|
WajubProvider | Loads SDK once for the subtree |
CheckoutEmbed | Full hosted checkout inline |
CardComponent | Card fields only |
PaymentComponent | Method selector + form |
AddressComponent | Shipping / billing address |
MobileMoneyComponent | Mobile money form |
WalletComponent | Apple Pay / Google Pay |
useWajub() | Access loaded SDK (throws while loading) |
useWajubOptional() | Safe access (null during SSR) |
useLoadWajub() | Trigger SDK load manually |
useConfirmPayment() | Submit a mounted component |
Nuxt tips
- Always wrap checkout in
<ClientOnly>— SDK is browser-only. - Create sessions in
server/api/— never exposesk_…to the client. - One
WajubProviderper checkout page (or layout). loadWajub()returnsnullduring SSR — composables handle this.