メインコンテンツへスキップ
このページで

前提条件#

実装する前に Deuna App Wallet 決済処理を行う場合、以下のものを用意してください:

  • 有効 Deuna App Wallet Deuna 管理パネル内
  • Deuna API 注文トークン Deuna SDK
  • プロジェクトに プロジェクトに ご自身のプラットフォーム向けに
  • レビューを実施しました 支払いウィジェットのドキュメント 管理パネルで
  • 希望の 決済方法は現在有効になり、チェックアウト時にお客様に選択肢として表示されます。 (数値コードまたはQRコード)を選択する

決済ウィジェットの設定#

基本的な実装

決済ウィジェットを deuna_wallet プロセッサ:

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
    },
  },
});

すべてのオプションを含む完全な例

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,
      });
    },
  },
});

支払い方法#

2つのモードの理解

貴社の口座は、以下の設定で構成されています。 これらのモードのいずれか 管理パネルで:

モード 1:数値コード(3分間)

顧客が確認する内容:

  • 画面に表示される6桁の数字
  • 残りの時間を示すカウントダウンタイマー (0:00-3:00)
  • Deunaアプリを開き、コードを入力する手順

顧客フロー:

  1. 6桁のコードをメモ
  2. Deunaアプリを開く
  3. 「コードで支払う」を選択
  4. 6桁のコードを入力
  5. アプリで支払いを確定
  6. Webhook経由で即時確認

設定: 管理パネルの「Deuna App Wallet」プロセッサ設定で設定

モード2: QRコード (15分間有効)

顧客が確認する内容:

  • 画面に表示される動的なQRコード
  • 残り時間を表示するカウントダウンタイマー (0:00-15:00)
  • 以下のいずれかの方法で手続きを進めることができます:
    • DeunaアプリのカメラでQRコードをスキャン
    • QRをタップしてアプリを直接開く

顧客フロー:

  1. オプションA - スキャン:

    • Deunaアプリのカメラを開く
    • QRコードをスキャン
    • 支払いページが自動的に開く
    • アプリで支払いを確定
    • 支払い確認はWebhook経由
  2. オプションB - クリック:

    • QRコードまたはボタンをクリック
    • Deunaアプリを直接開く
    • 支払い確認画面を表示
    • 支払いを確定
    • 支払い確認はWebhook経由

設定: 管理パネルの「Deuna App Wallet」プロセッサ設定で設定

注: モードの選択 初期設定時に一度 管理パネルで設定します。 その後の注文は、設定されたモードを使用します。 モードの変更については、Deunaサポートにお問い合わせください。

支払い確認の処理#

Webhook を使用した確認 (主要)

Deuna App Wallet は、顧客が支払いを完了した際に、バックエンドに Webhook 通知を送信します。

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

ポーリングによる代替 (二次)

Webhook の配信が遅延または失敗した場合、システムは定期的に /order エンドポイント: 3秒ごとに

JavaScript
// 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();
};

レスポンスオブジェクト#

成功応答

決済が完了した場合、 onSuccess コールバックは以下の情報を受信します:

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

エラーレスポンス

決済が失敗した場合、 onError コールバックは以下の情報を受信します:

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

保留状態のレスポンス

ユーザーの確認を待っている場合、 onPending コールバックは以下の情報を受信します:

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
}

無効なレスポンス

コード/QRが無効になった場合、 onExpired コールバックは以下の情報を受信します:

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に関するベストプラクティス#

数値コードモードの場合

  • コードを明確に表示する - 大きく、読みやすいフォント
  • カウントダウンタイマーを表示する - Обновлять каждую секунду
  • 明確な指示を表示する:
    • 「Deunaアプリを開く」
    • 「コードを使用して支払う」を選択する
    • 「この6桁のコードを入力する」
    • 「支払いを確定する」
  • 有効期限の状態を表示する - コードが3分後に無効になった場合
  • 再生成オプションを提供する - 「新しいコードを生成」ボタン

QRコードモードの場合

  • QRコードを大きく表示する - 中央揃え、スケーラブル
  • カウントダウンタイマーを表示する - Обновлять каждую секунду
  • 以下の2つのインタラクションオプションを提供する:
    • DeunaアプリでQRコードをスキャン
    • QRをタップしてアプリを直接開く
  • 明確な指示を表示する
  • 有効期限の状態を表示する - 15分後にQRコードが期限切れになった場合
  • 再生成オプションを提供する - 「新しいコードを生成」ボタン

一般的なガイドライン

  • モーダル/画面を開いたままにする - 支払い完了または期限切れになるまで、リダイレクトまたはクローズしない
  • 他の支払い方法を無効にする - 待っている間、別の方法を選択できないようにする
  • モバイル対応 - 小さな画面での可読性を確保する
  • アクセシビリティ - QRコード、タイマーの明確な色コントラストのための代替テキストを含める
  • 読み込み状態 - コード/QRコードの生成中にスピンナーを表示

一般的な実装パターン#

React Hookパターン

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