Deuna App Walletをウィジェット経由で
前提条件#
実装する前に 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アプリを開き、コードを入力する手順
顧客フロー:
- 6桁のコードをメモ
- Deunaアプリを開く
- 「コードで支払う」を選択
- 6桁のコードを入力
- アプリで支払いを確定
- Webhook経由で即時確認
設定: 管理パネルの「Deuna App Wallet」プロセッサ設定で設定
モード2: QRコード (15分間有効)
顧客が確認する内容:
- 画面に表示される動的なQRコード
- 残り時間を表示するカウントダウンタイマー (0:00-15:00)
- 以下のいずれかの方法で手続きを進めることができます:
- DeunaアプリのカメラでQRコードをスキャン
- QRをタップしてアプリを直接開く
顧客フロー:
-
オプションA - スキャン:
- Deunaアプリのカメラを開く
- QRコードをスキャン
- 支払いページが自動的に開く
- アプリで支払いを確定
- 支払い確認はWebhook経由
-
オプション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 };
};