隨著網購普及,支付寶成為台灣/大陸電商不可或缺的支付方式。本文提供從申請商戶、簽名驗證、訂單建立、回調處理到退款流程的完整 PHP 實作指南(基於支付寶開放平台標準介面)。
AppID、應用私鑰、支付寶公鑰return_url、非同步通知 notify_url<?php
namespace App\Payment;
class Alipay {
private $config;
public function __construct(array $config) {
$this->config = array_merge([
'gateway' => 'https://openapi.alipay.com/gateway.do',
'charset' => 'UTF-8',
'sign_type' => 'RSA2',
'format' => 'JSON',
], $config);
}
// 產生待簽名字串
private function getSignContent(array $params): string {
ksort($params);
$str = '';
foreach ($params as $k => $v) {
if ($v !== '' && $k !== 'sign') {
$str .= "$k=$v&";
}
}
return rtrim($str, '&');
}
// RSA2 簽名
private function sign(string $data): string {
$privateKey = "-----BEGIN PRIVATE KEY-----\n" .
wordwrap($this->config['private_key'], 64, "\n", true) .
"\n-----END PRIVATE KEY-----";
$res = openssl_get_privatekey($privateKey);
openssl_sign($data, $signature, $res, OPENSSL_ALGO_SHA256);
openssl_free_key($res);
return base64_encode($signature);
}
// 驗證支付寶回調簽名
public function verify(array $params): bool {
$sign = $params['sign'] ?? '';
unset($params['sign'], $params['sign_type']);
$content = $this->getSignContent($params);
$publicKey = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap($this->config['alipay_public_key'], 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
$res = openssl_get_publickey($publicKey);
$ok = openssl_verify($content, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
openssl_free_key($res);
return $ok === 1;
}
// 建立表單自動提交(頁面支付)
public function buildForm(string $method, array $bizContent): string {
$params = [
'app_id' => $this->config['app_id'],
'method' => $method,
'charset' => $this->config['charset'],
'sign_type' => $this->config['sign_type'],
'timestamp' => date('Y-m-d H:i:s'),
'version' => '1.0',
'biz_content'=> json_encode($bizContent, JSON_UNESCAPED_UNICODE),
'notify_url' => $this->config['notify_url'],
'return_url' => $this->config['return_url'],
];
$params['sign'] = $this->sign($this->getSignContent($params));
$html = '<form id="alipay_form" action="' . $this->config['gateway'] . '" method="POST">';
foreach ($params as $k => $v) {
$html .= '<input type="hidden" name="' . $k . '" value="' . htmlspecialchars($v) . '">';
}
$html .= '</form><script>document.getElementById("alipay_form").submit();</script>';
return $html;
}
}
<?php
require 'Alipay.php';
$config = [
'app_id' => '202100xxxxxx',
'private_key' => 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD...',
'alipay_public_key'=> 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...',
'notify_url' => 'https://yourdomain.com/alipay/notify',
'return_url' => 'https://yourdomain.com/alipay/return',
];
$alipay = new Alipay($config);
$bizContent = [
'out_trade_no' => 'ORDER_' . date('YmdHis') . rand(1000,9999),
'total_amount' => '99.00',
'subject' => '測試商品 - PHP 整合支付寶',
'product_code' => 'FAST_INSTANT_TRADE_PAY',
];
echo $alipay->buildForm('alipay.trade.page.pay', $bizContent);
<?php
// notify.php
require 'Alipay.php';
$alipay = new Alipay($config);
if ($alipay->verify($_POST)) {
$tradeStatus = $_POST['trade_status'] ?? '';
$outTradeNo = $_POST['out_trade_no'] ?? '';
if (in_array($tradeStatus, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
// 1. 驗證金額、商戶單號是否符合預期
// 2. 更新訂單狀態為「已付款」
// 3. 發送確認通知、發貨邏輯
// 4. 記錄日誌
file_put_contents('alipay_notify.log', date('c') . " SUCCESS: $outTradeNo\n", FILE_APPEND);
}
echo 'success'; // 必須輸出 success,否則支付寶會重試通知
} else {
file_put_contents('alipay_notify.log', date('c') . " VERIFY FAIL\n", FILE_APPEND);
echo 'fail';
}
<?php
// return.php
require 'Alipay.php';
$alipay = new Alipay($config);
if ($alipay->verify($_GET)) {
echo "支付成功!訂單號: " . htmlspecialchars($_GET['out_trade_no']);
// 此處僅做頁面提示,實際訂單狀態以非同步通知為準
} else {
echo "簽名驗證失敗,請聯繫客服";
}
$refundResult = $alipay->execute('alipay.trade.refund', [
'out_trade_no' => 'ORDER_202401151200001234',
'refund_amount' => '50.00',
'refund_reason' => '客戶申請部分退款',
]);
// 解析 $refundResult 判斷退款是否成功
total_amount 必須與訂單金額一致支付寶介面整合核心在於「簽名驗證」與「非同步通知冪等處理」。建議生產環境直接使用官方 alipay-sdk-php,減少造輪子風險。本文提供的簡易封裝適合學習原理或輕量專案參考。