FPX(ファイナンシャルプロセスエクスチェンジ)
FPXはマレーシアの国家的銀行間決済ゲートウェイであり、19以上のマレーシア銀行からのインスタント送金を受け付けます。全国に広く採用されている決済ゲートウェイです。
概要
FPX(ファイナンシャルプロセスエクスチェンジ)はPayNetが運営するマレーシアの国家的オンライン銀行決済ゲートウェイです。銀行口座から直接のオンライン銀行経由での支払いが可能であり、マレーシアの電子商取引において最も人気のある支払い方法の一つです。
主な機能:
- ✅ 19行以上の銀行 - マレーシア主要銀行全て対応
- ✅ 国家システム - マレーシアの公式銀行間ゲートウェイ
- ✅ 即座の確認 - リアルタイム決済処理
- ✅ 高額制限 - 1取引につきRM30,000まで
- ✅ 登録不要 - 顧客は既存のオンライン銀行を使用
- ✅ 信頼性 - PayNet(マレーシア中央銀行)が運営
対応地域
| 地域 | 通貨 | 最小金額 | 最大金額 | APIバージョン |
|---|---|---|---|---|
| Malaysia | MYR | RM1.00 | RM30,000.00 | 2017-11-02 |
対応銀行
FPXは19以上のマレーシア主要銀行に対応:
主要銀行:
- Affin Bank
- Alliance Bank
- AmBank
- Bank Islam
- Bank Muamalat
- Bank Rakyat
- BSN(Bank Simpanan Nasional)
- CIMB Bank
- Hong Leong Bank
- HSBC Bank
- Kuwait Finance House
- Maybank
- OCBC Bank
- Public Bank
- RHB Bank
- Standard Chartered
- UOB(United Overseas Bank)
仕組み
顧客体験:
- 顧客がチェックアウトで「FPX」を選択
- FPXゲートウェイへリダイレクト
- リストから銀行を選択
- 銀行のオンライン銀行へリダイレクト
- オンライン銀行の認証情報でログイン
- 支払いを確認&認可
- 加盟店サイトに戻す
標準的な完了時間: 2~5分
支払いフローの例
銀行選択及びログイン:

FPXゲートウェイの認証段階:
- ❶ FPXを選択 - 顧客がチェックアウトでFPX銀行送金を選択
- ❷ FPXへリダイレクト - マレーシアのFXP決済ゲートウェイに移動
- ❸ 銀行を選択 - 参加している19以上の銀行から選択
- ❹ 銀行へリダイレクト - 選択した銀行のオンライン銀行ページに転送
- ❺ ログイン - オンライン銀行のユーザーネーム及びパスワードを入力
- ❻ 2FAの確認 - 2要素認証を完了(TAC/OTP)
- ❼ 認証完了 - 支払い認可に進む
支払い確認:

取引の認可及び完了:
- ❽ 詳細を確認 - 加盟店名、支払い金額、参照番号を表示
- ❾ 口座を確認 - 支払い元口座を確認
- ❿ 支払いを認可 - 承認ボタンをクリックして送金を承認
- ⓫ 処理中 - 銀行がFPX経由で銀行間送金を処理
- ⓬ 成功確認 - 支払い完了メッセージと取引IDを表示
- ⓭ 加盟店に戻す - 加盟店の確認ページへ戻す
実装
ステップ1:FPXソースを作成
- cURL
- Node.js
- PHP
- Python
- Ruby
- Go
- Java
- C#
curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=fpx" \
-d "amount=10000" \
-d "currency=MYR" \
-d "email=customer@example.com"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const source = await omise.sources.create({
type: 'fpx',
amount: 10000, // MYR 100.00
currency: 'MYR',
email: 'customer@example.com' // オプションだが推奨
});
<?php
$source = OmiseSource::create(array(
'type' => 'fpx',
'amount' => 10000,
'currency' => 'MYR',
'email' => 'customer@example.com'
));
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
source = omise.Source.create(
type='fpx',
amount=10000,
currency='MYR',
email='customer@example.com'
)
require 'omise'
Omise.api_key = 'skey_test_YOUR_SECRET_KEY'
source = Omise::Source.create({
type: 'fpx',
amount: 10000,
currency: 'MYR',
email: 'customer@example.com'
})
source, err := client.Sources().Create(&operations.CreateSource{
Type: "fpx",
Amount: 10000,
Currency: "MYR",
Email: "customer@example.com",
})
Source source = client.sources().create(new Source.CreateParams()
.type("fpx")
.amount(10000L)
.currency("MYR")
.email("customer@example.com"));
var source = await client.Sources.Create(new CreateSourceRequest
{
Type = "fpx",
Amount = 10000,
Currency = "MYR",
Email = "customer@example.com"
});
レスポンス:
{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "fpx",
"flow": "redirect",
"amount": 10000,
"currency": "MYR",
"email": "customer@example.com"
}
ステップ2 :請求を作成
curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=10000" \
-d "currency=MYR" \
-d "source=src_test_5rt6s9vah5lkvi1rh9c" \
-d "return_uri=https://yourdomain.com/payment/callback"
ステップ3:顧客をリダイレクト
app.post('/checkout/fpx', async (req, res) => {
try {
const { amount, order_id, customer_email } = req.body;
// 金額を検証
if (amount < 100 || amount > 3000000) {
return res.status(400).json({
error: 'RM1~RM30,000の間である必要があります'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'fpx',
amount: amount,
currency: 'MYR',
email: customer_email
});
// 請求を作成
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
}
});
// FPXへリダイレクト
res.redirect(charge.authorize_uri);
} catch (error) {
console.error('FPXエラー:', 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 === 'fpx') {
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/fpx', async (req, res) => {
try {
const { amount, order_id, customer_email } = req.body;
// 金額を検証(RM1~RM30,000)
if (amount < 100 || amount > 3000000) {
return res.status(400).json({
error: 'RM1~RM30,000の間である必要があります'
});
}
// メールアドレスを検証
if (!customer_email || !customer_email.includes('@')) {
return res.status(400).json({
error: 'FPX支払いに有効なメールアドレスが必要です'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'fpx',
amount: amount,
currency: 'MYR',
email: customer_email
});
// 請求を作成
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: 'fpx',
customer_email: customer_email
}
});
// 認可URLを返す
res.json({
authorize_uri: charge.authorize_uri,
charge_id: charge.id
});
} catch (error) {
console.error('FPXエラー:', 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 === 'fpx') {
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);
返金対応
FPXは 6カ月以内 の 全額及び部分的な返金 に対応:
// 全額返金
const fullRefund = await omise.charges.refund('chrg_test_...', {
amount: 10000
});
// 部分返金
const partialRefund = await omise.charges.refund('chrg_test_...', {
amount: 5000 // 半額返金
});
返金の有効期限
返金は元の取引後6カ月以内に対応します。全額及び部分的な返金の両方に対応しています。
一般的な問題&トラブルシューティング
問題:銀行が利用できない
原因: 顧客の銀行が一時的に利用できないか、メンテナンス中
解決方法:
- リアルタイム銀行の利用可能状況を表示(FPXが提供している場合)
- 代替支払い方法を表示
- 推定ダウンタイム情報を提供