WeChat Pay
WeChat Payを通じて、中国および世界中のWeChatの13億人以上のメッセージングアプリユーザーからの支払いを受け付けます。WeChatエコシステムにシームレスに統合された、中国の2大モバイル決済プラットフォームの1つです。
加盟店がQRコードを表示するPOS連携については、WeChat Pay MPMをご覧ください。加盟店が顧客の決済コードをスキャンするバーコードスキャンについては、WeChat Pay UPMをご覧ください。
概要
WeChat Pay(中国ではWeixin Pay)は、WeChatメッセージングアプリに直接統合されたTencentのモバイル決済ソリューションです。WeChatメッセージング(13億人以上のユーザー)と統合されており、WeChat Payは中国の2大決済プラットフォームの1つで、Alipayと並んで中国で最大の決済方法の1つです。WeChat Payは、QRコード、アプリ内購入、ミニプログラムを通じて即座の決済を可能にします。
主な機能:
- ✅ WeChatと統合 - 中国および海外の中国人コミュニティ全体で13億人以上のメッセージングアプリユーザーにアクセス
- ✅ WeChat統合 - メッセージングアプリエコシステム内でのシームレスな決済
- ✅ QRコード決済 - 高速で非接触の決済体験
- ✅ クロスボーダーサポート - 世界中の中国人観光客からの支払いを受け付け
- ✅ マルチ通貨 - THB、SGD、MYR、JPY、USDで決済
- ✅ ミニプログラム - WeChat内でeコマース体験を構築
サポートされている地域
| 地域 | 通貨 | 最小金額 | 最大金額 | APIバージョン |
|---|---|---|---|---|
| Thailand | THB | ฿20.00 | ฿150,000.00 | 2017-11-02 |
WeChat Payは、中国人観光客やクロスボーダーeコマースを対象とするマーチャントにとって特に価値があります。顧客はCNYで支払い、現地通貨で決済を受け取ります。
仕組み
顧客体験:
- 顧客がチェックアウト時に「WeChat Pay」を選択
- QRコードが画面に表示される
- 顧客がWeChatアプリを開いてQRコードをスキャン
- WeChatで取引詳細を確認
- WeChat Payパスワードまたは生体認証で支払いを確認
- マーチャントウェブサイトに戻る
- 支払い確認を受け取る
通常の完了時間: 30-90秒
実装
ステップ1: WeChat Payソースの作成
- cURL
- Node.js
- PHP
- Python
- Ruby
- Go
- Java
- C#
curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=wechat_pay" \
-d "amount=10000" \
-d "currency=THB"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const source = await omise.sources.create({
type: 'wechat_pay',
amount: 10000, // THB 100.00
currency: 'THB'
});
<?php
$source = OmiseSource::create(array(
'type' => 'wechat_pay',
'amount' => 10000,
'currency' => 'THB'
));
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
source = omise.Source.create(
type='wechat_pay',
amount=10000,
currency='THB'
)
require 'omise'
Omise.api_key = 'skey_test_YOUR_SECRET_KEY'
source = Omise::Source.create({
type: 'wechat_pay',
amount: 10000,
currency: 'THB'
})
source, err := client.Sources().Create(&operations.CreateSource{
Type: "wechat_pay",
Amount: 10000,
Currency: "THB",
})
Source source = client.sources().create(new Source.CreateParams()
.type("wechat_pay")
.amount(10000L)
.currency("THB"));
var source = await client.Sources.Create(new CreateSourceRequest
{
Type = "wechat_pay",
Amount = 10000,
Currency = "THB"
});
レスポンス:
{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "wechat_pay",
"flow": "redirect",
"amount": 10000,
"currency": "THB",
"scannable_code": {
"type": "qr",
"image": {
"uri": "https://omise.co/qr/...",
"download_uri": "https://api.omise.co/..."
}
}
}
ステップ2: 課金の作成
curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=10000" \
-d "currency=THB" \
-d "source=src_test_5rt6s9vah5lkvi1rh9c" \
-d "return_uri=https://yourdomain.com/payment/callback"
ステップ3: QRコードの表示
app.post('/checkout/wechat-pay', async (req, res) => {
try {
const { amount, order_id } = req.body;
// 金額を検証
if (amount < 100 || amount > 10000000) {
return res.status(400).json({
error: '金額は฿1から฿100,000の間である必要があります'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'wechat_pay',
amount: amount,
currency: 'THB'
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: 'THB',
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id
}
});
// QRコードURLを返して表示
res.json({
qr_code_url: charge.source.scannable_code.image.uri,
charge_id: charge.id,
expires_at: new Date(Date.now() + 5 * 60 * 1000) // 5分
});
} catch (error) {
console.error('WeChat Pay error:', error);
res.status(500).json({ error: error.message });
}
});
ステップ4: QRコードUI表示
<!DOCTYPE html>
<html>
<head>
<title>WeChat Pay チェックアウト</title>
<style>
.wechat-pay-container {
max-width: 400px;
margin: 50px auto;
text-align: center;
padding: 30px;
border: 1px solid #e0e0e0;
border-radius: 10px;
}
.qr-code {
width: 300px;
height: 300px;
margin: 20px auto;
border: 1px solid #ddd;
padding: 10px;
background: white;
}
.wechat-logo {
width: 60px;
height: 60px;
margin-bottom: 15px;
}
.countdown {
font-size: 18px;
color: #666;
margin-top: 15px;
}
.instructions {
color: #666;
margin: 20px 0;
line-height: 1.6;
}
</style>
</head>
<body>
<div class="wechat-pay-container">
<img src="/images/wechat-logo.svg" class="wechat-logo" alt="WeChat Pay">
<h2>WeChatでスキャンして支払う</h2>
<div class="qr-code">
<img id="qr-image" src="" alt="WeChat Pay QR Code" style="width: 100%; height: 100%;">
</div>
<div class="instructions">
<ol style="text-align: left;">
<li>スマートフォンでWeChatアプリを開く</li>
<li>「+」 アイコンをタップして「QRコードをスキャン」を選択</li>
<li>上記のQRコードをスキャン</li>
<li>WeChatで支払いを確認</li>
</ol>
</div>
<div class="countdown">
残り時間: <span id="timer">5:00</span>
</div>
<p style="color: #999; font-size: 14px; margin-top: 20px;">
支払い金額: <strong id="amount"></strong>
</p>
</div>
<script>
// カウントダウンタイマー
let timeLeft = 300; // 5分
const timerElement = document.getElementById('timer');
const countdown = setInterval(() => {
timeLeft--;
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
clearInterval(countdown);
alert('QRコードの有効期限が切れました。もう一度お試しください。');
window.location.href = '/checkout';
}
}, 1000);
// 支払いステータスをポーリング
const chargeId = new URLSearchParams(window.location.search).get('charge_id');
const checkStatus = setInterval(async () => {
try {
const response = await fetch(`/api/check-payment-status/${chargeId}`);
const data = await response.json();
if (data.status === 'successful') {
clearInterval(checkStatus);
clearInterval(countdown);
window.location.href = '/payment-success';
} else if (data.status === 'failed') {
clearInterval(checkStatus);
clearInterval(countdown);
window.location.href = '/payment-failed';
}
} catch (error) {
console.error('Status check error:', error);
}
}, 3000); // 3秒ごとにチェック
// QRコードを読み込む
fetch('/api/create-wechat-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 10000,
order_id: 'ORD-12345'
})
})
.then(res => res.json())
.then(data => {
document.getElementById('qr-image').src = data.qr_code_url;
document.getElementById('amount').textContent = `฿${(data.amount / 100).toFixed(2)}`;
});
</script>
</body>
</html>
ステップ5: Webhookの処理
app.post('/webhooks/omise', (req, res) => {
const event = req.body;
if (event.key === 'charge.complete' && event.data.source.type === 'wechat_pay') {
const charge = event.data;
if (charge.status === 'successful') {
processOrder(charge.metadata.order_id);
sendConfirmationEmail(charge.metadata.customer_email);
} else if (charge.status === 'failed') {
handleFailedPayment(charge.metadata.order_id);
}
}
res.sendStatus(200);
});
完全な実装例
// Express.jsサーバー
const express = require('express');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});
const app = express();
app.use(express.json());
// WeChat Pay支払いを作成
app.post('/api/create-wechat-payment', async (req, res) => {
try {
const { amount, order_id, customer_email } = req.body;
// 金額を検証(฿1 - ฿100,000)
if (amount < 100 || amount > 10000000) {
return res.status(400).json({
error: '金額は฿1から฿100,000の間である必要があります'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'wechat_pay',
amount: amount,
currency: 'THB'
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: 'THB',
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id,
customer_email: customer_email,
payment_method: 'wechat_pay'
}
});
// QRコードを返す
res.json({
charge_id: charge.id,
qr_code_url: charge.source.scannable_code.image.uri,
amount: charge.amount,
expires_at: new Date(Date.now() + 5 * 60 * 1000)
});
} catch (error) {
console.error('WeChat Pay error:', error);
res.status(500).json({ error: error.message });
}
});
// 支払いステータスを確認(ポーリング用)
app.get('/api/check-payment-status/:chargeId', async (req, res) => {
try {
const charge = await omise.charges.retrieve(req.params.chargeId);
res.json({
status: charge.status,
paid: charge.paid
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Webhookハンドラー(主要な通知方法)
app.post('/webhooks/omise', (req, res) => {
const event = req.body;
if (event.key === 'charge.complete') {
const charge = event.data;
if (charge.source.type === 'wechat_pay') {
if (charge.status === 'successful') {
updateOrderStatus(charge.metadata.order_id, 'paid');
sendConfirmationEmail(charge.metadata.customer_email);
// 成功した支払いをログ
console.log(`WeChat Pay payment successful: ${charge.id}`);
} else {
updateOrderStatus(charge.metadata.order_id, 'failed');
// 失敗した支払いをログ
console.log(`WeChat Pay payment failed: ${charge.id}, reason: ${charge.failure_message}`);
}
}
}
res.sendStatus(200);
});
// ヘルパー関数
async function updateOrderStatus(orderId, status) {
// データベースで注文を更新
await db.orders.update({ id: orderId }, { status: status });
}
async function sendConfirmationEmail(email) {
// 確認メールを送信
// 実装はメールサービスに依存
}
app.listen(3000, () => {
console.log('Server running on port 3000');
});
返金サポート
WeChat Payは90日以内の全額および一部返金をサポートしています:
// 全額返金
const fullRefund = await omise.charges.refund('chrg_test_...', {
amount: 10000
});
// 一部返金
const partialRefund = await omise.charges.refund('chrg_test_...', {
amount: 5000 // 半額返金
});
- 返金は1〜3営業日以内に処理されます
- 顧客はWeChatウォレットで返金を受け取ります
- 元の取引から90日以内にサポート
返金期間とポリシーは変更される場合があります。Omise APIドキュメントまたはマーチャントダッシュボードで常に現在の返金機能を確認してください。
よくある問題とトラブルシューティング
問題: QRコードがスキャンできない
原因: 顧客が非互換のQRスキャナーを使用しているか、コードの有効期限が切れている
解決策:
// QRコードが新鮮で有効であることを確認
function displayQRCode(qrCodeUrl, expiresAt) {
const qrImage = document.getElementById('qr-code');
qrImage.src = qrCodeUrl;
// 有効期限の警告を表示
const timeUntilExpiry = new Date(expiresAt) - Date.now();
if (timeUntilExpiry < 60000) { // 1分未満
showWarning('QRコードがまもなく期限切れになります!今すぐスキャンしてください。');
}
}
// 更新オプションを提供
function refreshQRCode() {
// 新しいQRコードを生成
createNewPayment();
}
問題: 支払いタイムアウト
原因: 顧客が5分以内に支払いを完了しなかった
解決策:
// 適切なタイムアウトを設定
const QR_EXPIRY_TIME = 5 * 60 * 1000; // 5分
setTimeout(() => {
if (!paymentConfirmed) {
showMessage('支払いセッションが期限切れになりました。新しい支払いを作成してください。');
enableRetry();
}
}, QR_EXPIRY_TIME);
問題: モバイルでWeChatアプリが開かない
原因: モバイルブラウザでのディープリンク問題
解決策:
function isMobileDevice() {
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
if (isMobileDevice()) {
// WeChatアプリを直接開こうとする
window.location = `weixin://dl/business/?t=${encodeURIComponent(qrCodeData)}`;
// QRコード表示へのフォールバック
setTimeout(() => {
if (document.hidden === false) {
displayQRCodeFallback();
}
}, 2000);
} else {
// デスクトップ: QRコードを表示
displayQRCode();
}