Skip to main content
On this page

Prerequisites#

Before implementing Deuna (Banco Pichincha) payments, ensure you have:

  • Enabled Deuna (Banco Pichincha) in the DEUNA Admin Panel
  • Generated an order token via the DEUNA API
  • Integrated the DEUNA SDK in your project
  • Reviewed the Payment Widget documentation 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:

JavaScript
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

JavaScript
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. DEUNA processes the final payment state asynchronously

Configuration: Set in Admin Panel under Deuna (Banco Pichincha) 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
    • DEUNA processes the final payment state asynchronously
  2. Option B - Click:

    • Clicks QR code or button
    • Opens Deuna app directly
    • Shows payment confirmation screen
    • Confirms payment
    • DEUNA processes the final payment state asynchronously

Configuration: Set in Admin Panel under Deuna (Banco Pichincha) 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#

Deuna (Banco Pichincha) does not define a separate merchant webhook payload. It follows the standard asynchronous payment-status processing model.

Use the widget callback to update the customer interface, then retrieve the original order and make fulfillment decisions from its final payment status. If the result remains unresolved, poll the order endpoint with a bounded cadence:

JavaScript
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:

JavaScript
{
  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:

JavaScript
{
  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:

JavaScript
{
  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:

JavaScript
{
  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

JavaScript
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 };
};