ShopeePay QR
ShopeePayからQRコード決済を受け付けます。東南アジア最大の電子商取引プラットフォームに統合されたデジタルウォレットで、1億5,000万人以上のアクティブユーザーがいます。
アプリ間連携をお探しですか?
ShopeePayアプリへの直接ディープリンクを使用したモバイルアプリ連携については、ShopeePay Jump Appをご覧ください。
決済フロー

この画像は顧客の旅を示しています: ウォレットの選択、QR codeのスキャン、概要の確認、決済の確認。
概要
ShopeePayは、東南アジア最大の電子商取引プラットフォームの1つであるShopeeアプリエコシステム内の決済ウォレットです。ユーザーはShopeeプラットフォーム内外で即座に安全な取引のためにShopeePayの残高で支払うことができます。
主な機能:
- ✅ 大規模なリーチ - 1億5,000万人以上のアクティブなShopeeユーザー
- ✅ 即時確認 - リアルタイム決済処理
- ✅ モバイルファースト - シームレスなモバイル決済体験
- ✅ 信頼されたプラットフォーム - Sea Group(NYSE: SE)の一部
- ✅ 地域カバレッジ - 複数国での利用可能性
- ✅ キャッシュバック報酬 - ユーザーはShopeeコインを獲得
サポート地域
| Region | Currency | Min Amount | Max Amount | API Version |
|---|---|---|---|---|
| Thailand | THB | ฿20.00 | ฿150,000.00 | 2017-11-02 |
| Singapore | SGD | $1.00 | $20,000.00 | 2017-11-02 |
| Malaysia | MYR | RM1.00 | RM4,999.00 | 2017-11-02 |
取引制限
タイ(THB)
| 認証レベル | 取引あたり | 1日の上限 | 月間上限 |
|---|---|---|---|
| ベーシック(電話のみ) | ฿50,000 | ฿50,000 | ฿200,000 |
| プラス(ID認証済み) | ฿50,000 | ฿200,000 | ฿500,000 |
マレーシア(MYR)
| 認証レベル | 取引あたり | 1日の上限 | 月間上限 |
|---|---|---|---|
| ベーシック(電話のみ) | RM1,500 | RM1,500 | RM3,000 |
| プラス(ID認証済み) | RM1,500 | RM5,000 | RM10,000 |
仕組み
顧客体験:
- 顧客がチェックアウトでShopeePayを選択
- ShopeePay認証ページにリダイレクト
- Shopeeアプリを開く(deep link)
- 決済詳細を確認
- PINまたは生体認証で認証
- 決済を確認
- 加盟店サイトに戻る
通常の完了時間: 1〜2分
実装
ステップ1: ShopeePayソースを作成
- cURL
- Node.js
- PHP
- Python
- Ruby
- Go
- Java
- C#
curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=shopeepay" \
-d "amount=25000" \
-d "currency=THB"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const source = await omise.sources.create({
type: 'shopeepay',
amount: 25000, // THB 250.00
currency: 'THB'
});
<?php
$source = OmiseSource::create(array(
'type' => 'shopeepay',
'amount' => 25000,
'currency' => 'THB'
));
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
source = omise.Source.create(
type='shopeepay',
amount=25000,
currency='THB'
)
require 'omise'
Omise.api_key = 'skey_test_YOUR_SECRET_KEY'
source = Omise::Source.create({
type: 'shopeepay',
amount: 25000,
currency: 'THB'
})
source, err := client.Sources().Create(&operations.CreateSource{
Type: "shopeepay",
Amount: 25000,
Currency: "THB",
})
Source source = client.sources().create(new Source.CreateParams()
.type("shopeepay")
.amount(25000L)
.currency("THB"));
var source = await client.Sources.Create(new CreateSourceRequest
{
Type = "shopeepay",
Amount = 25000,
Currency = "THB"
});
レスポンス:
{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "shopeepay",
"flow": "redirect",
"amount": 25000,
"currency": "THB"
}
ステップ2: 課金を作成
curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=25000" \
-d "currency=THB" \
-d "source=src_test_5rt6s9vah5lkvi1rh9c" \
-d "return_uri=https://yourdomain.com/payment/callback"
ステップ3: 顧客をリダイレクト
app.post('/checkout/shopeepay', async (req, res) => {
try {
const { amount, currency, order_id } = req.body;
// 通貨を検証
if (!['THB', 'MYR'].includes(currency)) {
return res.status(400).json({
error: 'ShopeePay supports THB and MYR only'
});
}
// 通貨別に金額を検証
const limits = {
THB: { min: 2000, max: 5000000 },
MYR: { min: 100, max: 150000 }
};
const { min, max } = limits[currency];
if (amount < min || amount > max) {
return res.status(400).json({
error: `Amount must be between ${min} and ${max} ${currency}`
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'shopeepay',
amount: amount,
currency: currency
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: currency,
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id
}
});
// ShopeePayにリダイレクト
res.redirect(charge.authorize_uri);
} catch (error) {
console.error('ShopeePay error:', error);
res.status(500).json({ error: error.message });
}
});
ステップ4: 返却を処理
app.get('/payment/callback', async (req, res) => {
try {
const chargeId = req.query.charge_id;
const charge = await omise.charges.retrieve(chargeId);
if (charge.status === 'successful') {
await processOrder(charge.metadata.order_id);
res.redirect('/payment-success');
} else if (charge.status === 'failed') {
res.redirect('/payment-failed?reason=' + charge.failure_message);
} else {
res.redirect('/payment-pending');
}
} catch (error) {
res.redirect('/payment-error');
}
});
ステップ5: Webhookを処理
app.post('/webhooks/omise', (req, res) => {
const event = req.body;
if (event.key === 'charge.complete' && event.data.source.type === 'shopeepay') {
const charge = event.data;
if (charge.status === 'successful') {
processOrder(charge.metadata.order_id);
} 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());
// 通貨別の金額制限
const LIMITS = {
THB: { min: 2000, max: 5000000 },
MYR: { min: 100, max: 150000 }
};
app.post('/checkout/shopeepay', async (req, res) => {
try {
const { amount, currency, order_id } = req.body;
// 通貨を検証
if (!['THB', 'MYR'].includes(currency)) {
return res.status(400).json({
error: 'ShopeePay only supports THB and MYR'
});
}
// 金額を検証
const { min, max } = LIMITS[currency];
if (amount < min || amount > max) {
return res.status(400).json({
error: `Amount must be between ${min} and ${max} ${currency}`
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'shopeepay',
amount: amount,
currency: currency
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: currency,
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id,
payment_method: 'shopeepay'
}
});
// 承認URLを返す
res.json({
authorize_uri: charge.authorize_uri,
charge_id: charge.id
});
} catch (error) {
console.error('ShopeePay error:', error);
res.status(500).json({ error: error.message });
}
});
// コールバックハンドラー
app.get('/payment/callback', async (req, res) => {
try {
const chargeId = req.query.charge_id;
const charge = await omise.charges.retrieve(chargeId);
if (charge.status === 'successful') {
res.redirect(`/order-success?order=${charge.metadata.order_id}`);
} else {
res.redirect(`/payment-failed?charge=${chargeId}`);
}
} catch (error) {
res.redirect('/payment-error');
}
});
// Webhookハンドラー
app.post('/webhooks/omise', (req, res) => {
const event = req.body;
if (event.key === 'charge.complete') {
const charge = event.data;
if (charge.source.type === 'shopeepay') {
if (charge.status === 'successful') {
updateOrderStatus(charge.metadata.order_id, 'paid');
sendConfirmation(charge.metadata.customer_email);
} else {
updateOrderStatus(charge.metadata.order_id, 'failed');
}
}
}
res.sendStatus(200);
});
app.listen(3000);
無効化と返金サポート
課金の無効化
ShopeePayは24時間以内の無効化をサポートしています:
// すぐに無効化(全額)
const refund = await omise.charges.refund('chrg_test_...', {
amount: 25000
});
if (refund.voided) {
console.log('Charge was voided (within 24 hours)');
}
返金
30日以内の全額返金のみ:
// 全額返金のみ
const refund = await omise.charges.refund('chrg_test_...', {
amount: 25000 // 全額である必要があります
});
部分返金なし
ShopeePayは部分返金をサポートしていません。30日以内の全額返金のみが許可されています。
よくある問題とトラブルシューティング
問題: 顧客がShopeeアプリを持っていない
原因: 顧客がShopeePayを選択したが、Shopeeアプリを持っていない
解決策:
function checkShopeeApp() {
if (!/Android|iPhone|iPad|iPod/i.test(navigator.userAgent)) {
alert('ShopeePayにはShopeeモバイルアプリが必要です。モバイルデバイスを使用してください。');
return false;
}
return true;
}
問題: 残高不足
エラー: 決済が拒否されました
解決策:
if (charge.failure_code === 'insufficient_balance') {
showMessage('ShopeePay残高が不足しています。Shopeeアプリでトップアップしてください。');
offerAlternativePayment();
}
問題: 決済タイムアウト
解決策:
const TIMEOUT = 15 * 60 * 1000; // 15分
setTimeout(() => {
if (!paymentConfirmed) {
showTimeoutMessage();
allowRetry();
}
}, TIMEOUT);