---
updatedAt: 2026-08-08T00:04:36.000Z
---

Fetch the complete documentation index at: https://docs.deuna.com/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Deuna App Wallet via Widget

## Prerequisites

Before implementing **Deuna App Wallet** payments, ensure you have:

* Enabled **Deuna App Wallet** in the Deuna Admin Panel
* Generated an **order token** via the Deuna API
* Integrated the **[Deuna SDK](https://docs.deuna.com/reference/deuna-sdks)** in your project
* Reviewed the **[Payment Widget documentation](https://docs.deuna.com/docs/payment-widget)** for your platform
* Selected your preferred **payment mode** (Numeric Code or QR) in the Admin Panel

## Payment Widget Configuration

### Basic Implementation

Initialize the Payment Widget with the `deuna_wallet` processor:

```typescript
DeunaSDK.initPaymentWidget({
  orderToken: '',
  paymentMethods: [
    {
      paymentMethod: 'voucher',
      processors: ['deuna_wallet'],
    },
  ],
  callbacks: {
    onSuccess: (data) => {
      console.log('Payment successful:', data);
      // Redirect to success page
    },
    onError: (error) => {
      console.log('Payment failed:', error);
      // Show error message to user
    },
    onPending: (data) => {
      console.log('Payment pending confirmation:', data);
      // Show waiting screen
    },
  },
});
```

### Complete Example with All Options

```typescript
DeunaSDK.initPaymentWidget({
  orderToken: '',
  paymentMethods: [
    {
      paymentMethod: 'voucher',
      processors: ['deuna_wallet'],
      metadata: {
        // Optional: Pass additional data
        orderId: 'your-internal-order-id',
      },
    },
  ],
  callbacks: {
    onSuccess: (response) => {
      console.log('Payment confirmed:', {
        orderId: response.order_id,
        orderToken: response.order_token,
        status: response.status,
        amount: response.amount,
        currency: response.currency,
      });
      
      // Verify payment on your backend
      fetch('/api/verify-payment', {
        method: 'POST',
        body: JSON.stringify({
          orderToken: response.order_token,
          orderId: response.order_id,
        }),
      })
      .then(res => res.json())
      .then(data => {
        if (data.verified) {
          window.location.href = '/order-confirmation';
        }
      });
    },
    
    onError: (error) => {
      console.error('Payment error:', {
        code: error.code,
        message: error.message,
      });
      
      // Show user-friendly error message
      showErrorModal({
        title: 'Payment Failed',
        message: 'Unable to process your payment. Please try again.',
      });
    },
    
    onPending: (response) => {
      console.log('Payment pending user confirmation:', {
        orderId: response.order_id,
        expiresAt: response.expires_at,
      });
    },
    
    onExpired: (response) => {
      console.log('Payment code/QR expired:', {
        orderId: response.order_id,
        expiredAt: response.expired_at,
      });
    },
  },
});
```

## Payment Modes

### Understanding the Two Modes

Your merchant account is configured with **one of these modes** in the Admin Panel:

#### Mode 1: Numeric Code (3-Minute Window)

**What the customer sees:**

* A 6-digit numeric code displayed on screen
* Countdown timer showing remaining time (0:00-3:00)
* Instructions to open Deuna App and enter the code

**Customer flow:**

1. Notes the 6-digit code
2. Opens Deuna App
3. Selects "Pay with Code"
4. Enters the 6-digit code
5. Confirms payment in app
6. Payment confirmed immediately via webhook

**Configuration:** Set in Admin Panel under Deuna App Wallet processor settings

#### Mode 2: QR Code (15-Minute Window)

**What the customer sees:**

* A dynamic QR code on screen
* Countdown timer showing remaining time (0:00-15:00)
* Two options to proceed:
  * Scan QR with Deuna App camera
  * Click QR to open app directly

**Customer flow:**

1. **Option A - Scan:**
   * Opens Deuna App camera
   * Scans the QR code
   * Payment page opens automatically
   * Confirms payment in app
   * Payment confirmed via webhook

2. **Option B - Click:**
   * Clicks QR code or button
   * Opens Deuna App directly
   * Shows payment confirmation screen
   * Confirms payment
   * Payment confirmed via webhook

**Configuration:** Set in Admin Panel under Deuna App Wallet processor settings

> **Note:** Mode selection happens **once during initial setup** in the Admin Panel. All subsequent orders will use the configured mode. Contact Deuna support to change modes.

## Handling Payment Confirmation

### Webhook-Based Confirmation (Primary)

The Deuna App Wallet sends a webhook notification to your backend when the customer confirms payment:

```typescript
// Your backend webhook endpoint
app.post('/webhooks/deuna-wallet', (req, res) => {
  const { order_id, order_token, status, amount, currency } = req.body;
  
  // Verify webhook signature (recommended)
  const isValid = verifyWebhookSignature(req);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Handle confirmation
  if (status === 'confirmed') {
    // Update your database
    updateOrder(order_id, {
      status: 'paid',
      paidAt: new Date(),
      amount: amount,
      currency: currency,
    });
    
    // Send confirmation email to customer
    sendConfirmationEmail(order_id);
  }
  
  // Acknowledge receipt
  res.json({ success: true });
});
```

### Polling Fallback (Secondary)

If webhook delivery is delayed or fails, the system polls the `/order` endpoint every 3 seconds:

```typescript
// Client-side polling (handled automatically by SDK)
// But you can also implement it manually if needed

const pollOrderStatus = async (orderToken) => {
  const maxAttempts = 60; // 3 minutes for numeric code
  let attempts = 0;
  
  while (attempts < maxAttempts) {
    try {
      const response = await fetch(`/api/order/${orderToken}`);
      const order = await response.json();
      
      if (order.status === 'confirmed') {
        handlePaymentSuccess(order);
        return;
      }
      
      if (order.status === 'expired') {
        handlePaymentExpired(order);
        return;
      }
      
      // Wait 3 seconds before polling again
      await new Promise(resolve => setTimeout(resolve, 3000));
      attempts++;
      
    } catch (error) {
      console.error('Polling error:', error);
    }
  }
  
  handlePaymentExpired();
};
```

## Response Objects

### Success Response

When payment is confirmed, `onSuccess` callback receives:

```typescript
{
  order_id: "order_abc123def456",
  order_token: "token_xyz789",
  status: "confirmed",
  payment_method: "deuna_wallet",
  amount: 99.99,
  currency: "USD",
  timestamp: "2024-03-20T14:30:45Z",
  metadata: {
    // Any metadata passed during initialization
  }
}
```

### Error Response

When payment fails, `onError` callback receives:

```typescript
{
  code: "PAYMENT_FAILED",
  message: "Unable to process payment",
  order_id: "order_abc123def456",
  details: {
    reason: "Insufficient funds in Deuna wallet"
  }
}
```

### Pending Response

When waiting for user confirmation, `onPending` callback receives:

```typescript
{
  order_id: "order_abc123def456",
  order_token: "token_xyz789",
  status: "pending",
  payment_method: "deuna_wallet",
  code_or_qr: "123456", // For numeric code mode
  expires_at: "2024-03-20T14:33:45Z",
  expires_in_seconds: 180
}
```

### Expired Response

When code/QR expires, `onExpired` callback receives:

```typescript
{
  order_id: "order_abc123def456",
  order_token: "token_xyz789",
  status: "expired",
  payment_method: "deuna_wallet",
  expired_at: "2024-03-20T14:33:45Z",
  message: "Code has expired"
}
```

## UI Best Practices

### For Numeric Code Mode

* **Display the code prominently** - Large, clear font
* **Show countdown timer** - Update every second
* **Display clear instructions:**
  * "Open the Deuna App"
  * "Select Pay with Code"
  * "Enter this 6-digit code"
  * "Confirm payment"
* **Show expiration state** - When code expires after 3 minutes
* **Provide regenerate option** - "Generate new code" button

### For QR Code Mode

* **Display QR code prominently** - Centered, scalable
* **Show countdown timer** - Update every second
* **Provide two interaction options:**
  * Scan QR with Deuna App
  * Click QR to open app directly
* **Display clear instructions**
* **Show expiration state** - When QR expires after 15 minutes
* **Provide regenerate option** - "Generate new code" button

### General Guidelines

* **Keep modal/screen open** - Do not redirect or close until payment confirmed or expired
* **Disable other payment methods** - Prevent selecting another method while waiting
* **Mobile responsive** - Ensure readability on small screens
* **Accessibility** - Include alt text for QR, clear color contrast for timers
* **Loading states** - Show spinner while generating code/QR

## Common Implementation Patterns

### React Hook Pattern

```typescript
import { useEffect, useState } from 'react';

export const useDeunaWalletPayment = (orderToken) => {
  const [status, setStatus] = useState('idle'); // idle | pending | success | error | expired
  const [expiresAt, setExpiresAt] = useState(null);

  useEffect(() => {
    if (!orderToken) return;

    DeunaSDK.initPaymentWidget({
      orderToken,
      paymentMethods: [
        {
          paymentMethod: 'voucher',
          processors: ['deuna_wallet'],
        },
      ],
      callbacks: {
        onPending: (data) => {
          setStatus('pending');
          setExpiresAt(data.expires_at);
        },
        onSuccess: (data) => {
          setStatus('success');
        },
        onError: (error) => {
          setStatus('error');
        },
        onExpired: () => {
          setStatus('expired');
        },
      },
    });
  }, [orderToken]);

  const regenerate = () => {
    setStatus('idle');
    window.location.reload();
  };

  return { status, expiresAt, regenerate };
};
```

<br />

<br />