Touch 'n Go eWallet
マレーシアで最も人気のあるデジタルウォレットTouch 'n Go eWalletから支払いを受け付けます。1700万人以上のユーザーと全国の広範な加盟店ネットワークを持っています。
概要
ユーザー統計
ユーザー数は概算であり、公開されている情報に基づいています。実際のアクティブユーザー数は異なる場合があります。
Touch 'n Go eWallet(TNG eWallet)はマレーシアの主要なデジタル決済プラットフォームで、もともと高速道路の通行料金で知られていましたが、現在はフル機能のデジタルウォレットに拡大しています。ユーザーはTNG eWalletの残高で、オンラインとオフラインの両方で即座に安全な取引を行うことができます。
主な特徴:
- ✅ 1700万人以上のユーザー - マレーシアで最も人気のあるeウォレット
- ✅ 即時確認 - リアルタイムの支払い処理
- ✅ 広範な採用 - マレーシア全土で受入
- ✅ 信頼されたブランド - Touch 'n Go Groupの一部
- ✅ 取引手数料なし - 顧客向け
- ✅ リワードプログラム - ユーザーはGO+ポイントを獲得
サポート地域
| Region | Currency | Min Amount | Max Amount | API Version |
|---|---|---|---|---|
| Malaysia | MYR | RM1.00 | RM9,999.99 | 2017-11-02 |
確認レベル別の取引制限
| 確認レベル | 1取引あたり | 日次制限 | 月次制限 |
|---|---|---|---|
| Lite (電話 + IC) | RM1,500 | RM1,500 | RM10,000 |
| Full (銀行リンク済み) | RM30,000 | RM30,000 | RM100,000 |
仕組み
顧客体験:
- 顧客がチェックアウトで「Touch 'n Go eWallet」を選択
- TNG認証ページにリダイレクト
- TNG eWalletアプリを開く(ディープリンク)
- 支払い詳細を確認
- 6桁のPINまたは生体認証で認証
- 支払いを確認
- 加盟店サイトに戻る
一般的な完了時間: 30秒〜2分
実装
ステップ1: Touch 'n Goソースを作成
- cURL
- Node.js
- PHP
- Python
- Ruby
- Go
- Java
- C#
curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=touch_n_go" \
-d "amount=15000" \
-d "currency=MYR"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const source = await omise.sources.create({
type: 'touch_n_go',
amount: 15000, // MYR 150.00
currency: 'MYR'
});
<?php
$source = OmiseSource::create(array(
'type' => 'touch_n_go',
'amount' => 15000,
'currency' => 'MYR'
));
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
source = omise.Source.create(
type='touch_n_go',
amount=15000,
currency='MYR'
)
require 'omise'
Omise.api_key = 'skey_test_YOUR_SECRET_KEY'
source = Omise::Source.create({
type: 'touch_n_go',
amount: 15000,
currency: 'MYR'
})
source, err := client.Sources().Create(&operations.CreateSource{
Type: "touch_n_go",
Amount: 15000,
Currency: "MYR",
})
Source source = client.sources().create(new Source.CreateParams()
.type("touch_n_go")
.amount(15000L)
.currency("MYR"));
var source = await client.Sources.Create(new CreateSourceRequest
{
Type = "touch_n_go",
Amount = 15000,
Currency = "MYR"
});
レスポンス:
{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "touch_n_go",
"flow": "redirect",
"amount": 15000,
"currency": "MYR"
}
ステップ2: 課金を作成
curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=15000" \
-d "currency=MYR" \
-d "source=src_test_5rt6s9vah5lkvi1rh9c" \
-d "return_uri=https://yourdomain.com/payment/callback"
ステップ3: 顧客をリダイレクト
app.post('/checkout/touch-n-go', async (req, res) => {
try {
const { amount, order_id } = req.body;
// 金額を検証
if (amount < 100 || amount > 3000000) {
return res.status(400).json({
error: 'Amount must be between RM1 and RM30,000'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'touch_n_go',
amount: amount,
currency: 'MYR'
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: 'MYR',
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id
}
});
// Touch 'n Goにリダイレクト
res.redirect(charge.authorize_uri);
} catch (error) {
console.error('Touch n Go 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 === 'touch_n_go') {
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());
app.post('/checkout/touch-n-go', async (req, res) => {
try {
const { amount, order_id } = req.body;
// 金額を検証 (RM1 - RM30,000)
if (amount < 100 || amount > 3000000) {
return res.status(400).json({
error: 'Amount must be between RM1 and RM30,000'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'touch_n_go',
amount: amount,
currency: 'MYR'
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: 'MYR',
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id,
payment_method: 'touch_n_go'
}
});
// 認証URLを返す
res.json({
authorize_uri: charge.authorize_uri,
charge_id: charge.id
});
} catch (error) {
console.error('TNG 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 === 'touch_n_go') {
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);
取消と返金のサポート
課金の取消
Touch 'n Goは24時間以内の取消をサポートします:
// 即座に取消(全額)
const refund = await omise.charges.refund('chrg_test_...', {
amount: 15000
});
if (refund.voided) {
console.log('Charge was voided (within 24 hours)');
}
返金
30日以内に全額返金のみ:
// 全額返金のみ
const refund = await omise.charges.refund('chrg_test_...', {
amount: 15000 // 全額である必要があります
});
一部返金なし
Touch 'n Go eWalletは一部返金をサポートしていません。30日以内に全額返金のみが許可されています。