Deuna (Banco Pichincha) 支払いウィジェット経由
Banco Pichincha が支援する Deuna ウォレットを DEUNA 支払いウィジェットと統合します。
前提条件#
導入前に Deuna (Banco Pichincha) お支払いの際は、次のものが揃っていることを確認してください。
- 有効 Deuna (Banco Pichincha) DEUNA 管理パネル内
- を生成しました 注文トークン DEUNA API経由
- を統合 DEUNA SDK あなたのプロジェクトで
- をレビューしました 支払いウィジェットのドキュメント あなたのプラットフォームのために
- ご希望を選択しました 支払いモード 管理パネルの(数値コードまたはQR)
支払いウィジェットの構成#
基本的な実装
支払いウィジェットを初期化します。 deuna_wallet プロセッサ:
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
},
},
});すべてのオプションを含む完全な例
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 つ 管理パネルで:
モード 1: 数値コード (3 分間のウィンドウ)
顧客が見ているもの:
- 画面に表示される6桁の数字コード
- 残り時間を表示するカウントダウンタイマー(0:00~3:00)
- Deuna アプリを開いてコードを入力する手順
お客様の流れ:
- 6桁のコードをメモします
- Deunaアプリを開く
- 「コードで支払う」を選択します
- 6桁のコードを入力します
- アプリ内で支払いを確認する
- DEUNA は最終的な支払い状態を非同期で処理します
構成: 管理パネルの Deuna (Banco Pichincha) プロセッサ設定で設定します。
モード 2: QR コード (15 分間のウィンドウ)
顧客が見ているもの:
- 画面上の動的な QR コード
- 残り時間を表示するカウントダウンタイマー(0:00~15:00)
- 続行するには 2 つのオプションがあります:
- DeunaアプリのカメラでQRをスキャン
- QRをクリックしてアプリを直接開きます
お客様の流れ:
-
オプション A - スキャン:
- Deuna アプリのカメラを開きます
- QRコードをスキャンします
- 支払いページが自動的に開きます
- アプリ内で支払いを確認する
- DEUNA は最終的な支払い状態を非同期で処理します
-
オプション B - クリックします:
- QRコードまたはボタンをクリックします
- Deuna アプリを直接開きます
- 支払い確認画面を表示します
- 支払いを確認します
- DEUNA は最終的な支払い状態を非同期で処理します
構成: 管理パネルの Deuna (Banco Pichincha) プロセッサ設定で設定します。
注: モード選択が発生します 初期セットアップ中に 1 回 管理者パネルで。後続のすべての注文では、構成されたモードが使用されます。モードを変更するには、DEUNA サポートにお問い合わせください。
支払い確認の処理#
Deuna (Banco Pichincha) は、個別のマーチャント Webhook ペイロードを定義していません。規格に準拠しています 非同期支払いステータス処理モデル.
ウィジェット コールバックを使用して顧客インターフェイスを更新し、元の注文を取得して、最終的な支払いステータスから履行の決定を行います。結果が未解決のままの場合は、制限された頻度で注文エンドポイントをポーリングします。
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 コールバックは以下を受け取ります:
{
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 コールバックは以下を受け取ります:
{
code: "PAYMENT_FAILED",
message: "Unable to process payment",
order_id: "order_abc123def456",
details: {
reason: "Insufficient funds in Deuna wallet"
}
}保留中の応答
ユーザーの確認を待っているとき、 onPending コールバックは以下を受け取ります:
{
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 コールバックは以下を受け取ります:
{
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 の生成中にスピナーを表示する
一般的な実装パターン#
反応フックパターン
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 };
};