npx claudepluginhub paid-tw/skills --plugin payuniThis skill is limited to using the following tools:
你的任務是在用戶的專案中實作統一金流 UPP 幕前支付功能。
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
你的任務是在用戶的專案中實作統一金流 UPP 幕前支付功能。
完成以下步驟即可完成串接:
詢問用戶:
框架類型:你使用什麼框架?
支付方式:需要支援哪些支付方式?(可複選)
用戶輸入: $ARGUMENTS
搜尋專案中的 .env 或設定檔,確認是否已設定:
PAYUNI_MERCHANT_IDPAYUNI_HASH_KEYPAYUNI_HASH_IV若未設定,引導用戶設定環境變數。
根據用戶框架建立支付模組檔案。
建立位置建議:
app/Services/PayuniService.phpservices/payuni.js 或 services/payuni.tslib/payuni.tspayments/services.py核心功能:
encrypt(data) - AES-256-CBC 加密decrypt(data) - AES-256-CBC 解密generateHashInfo(encryptInfo) - SHA256 雜湊createOrder(orderData) - 建立訂單並回傳表單資料verifyCallback(payload) - 驗證回調通知import crypto from 'crypto';
const config = {
merchantId: process.env.PAYUNI_MERCHANT_ID!,
hashKey: process.env.PAYUNI_HASH_KEY!,
hashIV: process.env.PAYUNI_HASH_IV!,
isTest: process.env.PAYUNI_TEST_MODE === 'true',
};
// AES-256-CBC 加密
function encrypt(data: string): string {
const key = Buffer.from(config.hashKey.padEnd(32, '\0').slice(0, 32), 'utf8');
const iv = Buffer.from(config.hashIV.padEnd(16, '\0').slice(0, 16), 'utf8');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
// SHA256 雜湊
function generateHashInfo(encryptInfo: string): string {
return crypto
.createHash('sha256')
.update(encryptInfo)
.digest('hex')
.toUpperCase();
}
// 建立訂單
function createOrder(params: {
orderId: string;
amount: number;
productName: string;
returnUrl: string;
notifyUrl: string;
}) {
const tradeInfo = {
MerID: config.merchantId,
MerTradeNo: params.orderId,
TradeAmt: params.amount,
ProdDesc: params.productName,
ReturnURL: params.returnUrl,
NotifyURL: params.notifyUrl,
};
const queryString = new URLSearchParams(tradeInfo as any).toString();
const encryptInfo = encrypt(queryString);
const hashInfo = generateHashInfo(encryptInfo);
return {
MerID: config.merchantId,
EncryptInfo: encryptInfo,
HashInfo: hashInfo,
};
}
根據框架建立支付表單,需包含:
<form method="post" action="https://sandbox-api.payuni.com.tw/api/upp">
<input type="hidden" name="MerID" value="{商店代號}">
<input type="hidden" name="EncryptInfo" value="{加密資料}">
<input type="hidden" name="HashInfo" value="{SHA256雜湊}">
<button type="submit">前往付款</button>
</form>
注意: 正式環境請改為 https://api.payuni.com.tw/api/upp
建立兩個端點:
NotifyURL (背景通知): POST /api/webhooks/payuni
{ success: true }ReturnURL (前台返回): GET /checkout/result
function verifyCheckCode(params: Record<string, string>): boolean {
const { CheckCode, ...otherParams } = params;
const sortedKeys = Object.keys(otherParams).sort();
const paramStr = sortedKeys.map(k => `${k}=${otherParams[k]}`).join('&');
const signStr = `HashKey=${config.hashKey}&${paramStr}&HashIV=${config.hashIV}`;
const calculated = crypto
.createHash('sha256')
.update(signStr)
.digest('hex')
.toUpperCase();
return calculated === CheckCode;
}
引導用戶進行測試:
https://sandbox-api.payuni.com.tw| 環境 | URL |
|---|---|
| 測試 | https://sandbox-api.payuni.com.tw/api/upp |
| 正式 | https://api.payuni.com.tw/api/upp |
| 參數 | 類型 | 說明 |
|---|---|---|
| MerID | String | 商店代號 |
| MerTradeNo | String(30) | 訂單編號(不可重複) |
| TradeAmt | Number | 金額 |
| ProdDesc | String | 商品描述 |
| ReturnURL | String | 前台返回網址 |
| NotifyURL | String | 背景通知網址 |
| 參數 | 值 | 說明 |
|---|---|---|
| CREDIT | 1 | 信用卡 |
| LINEPAY | 1 | LINE Pay |
| APPLEPAY | 1 | Apple Pay |
| GOOGLEPAY | 1 | Google Pay |
| VACC | 1 | ATM 轉帳 |
| CVS | 1 | 超商代碼 |
| BARCODE | 1 | 超商條碼 |