如何用PHP接入越南支付网关?
使用PHP接入越南支付网关的指南
要使用PHP接入越南支付网关,您需要遵循以下步骤。我将以几个流行的越南支付网关为例进行说明。
常见越南支付网关选择
- VNPay – 最受欢迎的本地支付解决方案之一
- Momo – 移动钱包支付
- ZaloPay – 另一个流行的电子钱包
- OnePay – Visa/MasterCard处理商
VNPay集成示例
1. 准备工作
- 注册VNPay商户账户
- 获取商户ID(
vnp_TmnCode
)和密钥(vnp_HashSecret
) - VNPay提供的API文档和端点URL
2. PHP集成代码示例
<?php
// config.php - VNPay配置参数
$vnp_Url = "https://sandbox.vnpayment.vn/paymentv2/vpcpay.html";
$vnp_Returnurl = "https://yourdomain.com/return_url.php"; //回调URL
$vnp_TmnCode = "YOUR_MERCHANT_CODE"; //商户号
$secretKey = "YOUR_SECRET_KEY"; //安全密钥
// vnpay_create_payment.php -创建付款请求
function createVNPayPayment($orderId, $amount, $orderInfo) {
global $vnp_Url, $vnp_Returnurl, $vnp_TmnCode, $secretKey;
date_default_timezone_set('Asia/Ho_Chi_Minh');
//准备参数数组
$inputData = array(
"version" => "2",
"command" => "pay",
'merchant' => 'VNPAY',
'tmn_code' => trim($tmnCode),
'create_date' => date('YmdHis'),
'expire_date' => date('YmdHis', strtotime('+15 minutes')),
//订单信息
'order_id' => time(),
//金额(单位为VND)
'amount'=> intval(str_replace(',','',$amount))*100,
//订单描述
'order_desc'=> urlencode($orderInfo),
return_url=> urlencode($return_url),
ip_address=> $_SERVER['REMOTE_ADDR'],
currency=>"VND",
locale=> ($lang == "") ? vn : en,
bank_code=> "",
);
ksort($inputData);
query="";
foreach ($inputData as key value){
if(substr(key0)=="_") continue;
if(!empty(query)) query.= &".key."=".$value; else query=key."=".$value;}
secureHash=hash("sha256", secret_key.query);
inputData["secure_hash"]=$secureHash;
paymentUrl=vpn_URL ."?".http_build_query(input_data);
header("Location: ".payment_url); exit();}
Momo Wallet集成示例
<?php
// momo_config.php
define("PARTNER_CODE", "");//Your partner code from MoMo");
define("ACCESS_KEY",""); ///Your access key from MoMo");
define("SECRET_KEY",""); ///Your secret key from MoMo");
function execPostRequest(url data){
ch curl_init();
curl_setopt(ch CURLOPT_URL url);
curl_setopt(ch CURLOPT_POST true);
curl_setopt(ch CURLOPT_POSTFIELDS data);
curl_setopt(ch CURLOPT_USERAGENT $_SERVER['HTTP_USER_AGENT']);
curl_setopt(ch CURLOPT_RETURNTRANSFER true);
result=curl_exec ch );curl_close ch ); return result;}
function createMomoPayment(orderId amount orderInfo callbackUrl redirectUrl){
endpoint ="https://test-payment.momo.vn/gw_payment/transactionProcessor";
requestId time()."";//unique id for each request
rawHash ="partnerCode=" PARTNER CODE.
&accessKey ACCESS KEY.
&requestId request Id.
&amount amount.
&orderId order Id .
order info .
return Url callback Url .
notify Url callback Url .extra Data "";
signature hash hmac sha256 raw Hash SECRET KEY));
data array(
partner Code PARTNER CODE ,
access Key ACCESS KEY ,
request Id request Id ,
amount amount ,
order Id order ID ,
Order Info urldecode(order Info ),
Return URL redirect URL ,
Notify URL call back URl ,
extra Data "",
Request Type capture MO wallet ",
Signature signature ) ;
JSON encode(data));
response exec Post Request(endpoint json encodedata));
json decode(response true);
if isset(result pay UrL )) header Location result pay UrL ]); else echo Error creating payment ;}?>
Zalo Pay集成示例(类似结构)
class ZalopayHelper {
const APP_ID your app id ;
const KEY1 your key1 ;
const EMBED DATA [" merchantinfo"=> embeddata json encode([" merchant name"=> Your Store Name ])];
public static function CreateOrder( string description long price string callback url ){
date default timezone set Asia Ho Chi Minh );
timestamp round(microtime(true)*1000);
params [
appid self APP ID,
apptransid date(Ymd)._. timestamp,# format yyMMdd timestamp e.g21092740765200
appuser Zalo Pay Demo m desc description,
amoun tprice ,
item JSON encode([]),
bankcode zalopayapp ,
callbackurl call back ur l ];
Mac=self Compute Mac(params);
params[ mac]=Mac;
response Http Helper post https sb openapi zalopay vn /vp/create params );
return json decode response body ) ;
}
private static function Compute Mac(array params ):string {
orderedParams [];
foreach ([ appid , apptransid , appuser amoun t apptime emb edda ta item ]as param){
if isset params param]) ordered Params[]=$param.'='.$params[$param];}
message implode(& ordered Params );
return hash hmac sha256 message self::KEY one false );}}
One Pay国际卡处理(适用于信用卡)
class One PayInternational {
private static endpoint sandbox onep ay international com api/v two /processor ;
public static process Payment card Number expiry Month expiry Year cv vAmount currency Description ){
date default timezone set Asia Ho Chi Minh );
nonce bin hex random bytes sixteen ));
post Fields [
number card Number ],
exp month expiry Month ],
exp year expiry Year ],cvv cv v],
currency str toupper currency ),
amoun tround float val Amount *100 ),# in cents/pence/etc.
description substr Description fifty ),
three DSecure false ];
json payload json encode post fields base sixty four encode nonce.json payload));
signature base sixty four encode hash hmac sha two five six nonce.payload YOUR API SECRE Ttrue));
headers [
Content Type application/json',
Authorization Bearer.YOUR API TOKEN ,
Signature.signature ];
ch init();
set opt ch,CUR LOP TSSL VERIFYPEERfalse);
set opt ch,CUR LOP THEADE R headers );
set optch CUR LOP POSTtrue);
set optch CUR LOP POS TFIELDS payload );
set optch CUR LOP RET UR NTR ANSFE Rtrue);
exec curlexe cch close (ch return response ;}}
?>"
重要提示:
所有上述代码仅为基本框架,实际实现时需注意:
安全性措施如输入验证、错误处理和日志记录必须到位。
生产环境应使用HTTPS并存储敏感数据在安全位置。
根据最新API文档调整参数名称和值格式。
建议先在各支付的沙盒环境中测试您的实现。
继续PHP接入越南支付网关的详细指南
下面我将更深入地介绍如何完善支付集成,包括回调处理、安全措施和常见问题的解决方案。
1. VNPay回调处理实现
当用户完成支付后,VNPay会通过两种方式通知你的系统:
- 浏览器重定向(return_url)
- 服务器异步通知(IPN – Instant Payment Notification)
return_url.php示例代码
<?php
// config.php
require_once('config.php');
// 获取所有GET参数
$vnp_Params = $_GET;
$secureHash = $vnp_Params['vnp_SecureHash'];
unset($vnp_Params['vnp_SecureHash']);
// 按字母顺序排序参数
ksort($vnp_Params);
// 生成验证用的hash值
$hashData = "";
foreach ($vnp_Params as $key => $value) {
if (substr($key, 0, 4) == "vnp_") {
$hashData .= urlencode($key) . "=" . urlencode($value) . "&";
}
}
$hashData = rtrim($hashData, '&');
$secureSecret = $secretKey; // from config.php
$mySecureHash = hash_hmac('sha512', $hashData, $secureSecret);
if ($mySecureHash === $secureHash) {
if ($_GET['vnP_ResponseCode'] == '00') {
// 付款成功逻辑
/*
* TODO:
* - 更新订单状态为已付款
* - 记录交易信息到数据库
* - vnP_TxnRef是您的订单ID(您最初传给VNPay的)
*/
echo "Payment Success for order: ".$_GET['vnP_TxnRef'];
} else {
// Payment failed or cancelled by user
/*
* TODO:
* - Update order status to failed/cancelled in your database
*/
echo "Payment Failed/Cancelled";
}
} else {
die("Invalid signature");
}
IPN处理脚本 (ipn_listener.php)
<?php
require_once('config.php');
file put contents ('ipn log.txt', print r($_POST true)."\n", FILE APPEND );
if (!empty($_POST)) {
input Data=$ POST;
secure Hash=input Data ['vn p Secure Hash'];
unset(input Data ['vn p Secure Hash']);
ksort(input Data);
i=0;
hash Data="";
foreach (input Data as key value){
if substr(key04)=="vn p"){
if strlen(hash Dat a)>0){
hash Dat a.='&'.url encode(key).'='.url encode(value);
}else{
hash Dat a.=url encode(key).'='.url encode(value);
}
}
}
check Sum=hash hmac sha512(h ash Da ta secret Key );
if check Sum== secure Has h){
transaction Id=input Da ta ['vp Txn Ref'];
response Code=input Da ta ['vp Response Co de'];
/* TODO:
1 Validate the amount matches your expected amount
2 Check this is not duplicate notification
3 Update your database accordingly */
switch(response Co de){ case"00": /* SUCCESS */ break ; default : /* FAILURE */ break ; }}else{ http response code(403);//Forbidden exit();}?>
MoMo钱包高级集成技巧
Webhook验证示例
function verify MomoWeb hook(Request request ){
partner Code request input('partner Code');
access Key request input('access Key');
request Id time()."";//should match original
order Id request input('orderId');
amount req uest->input amoun t));
order Info urldecode(req uest->input desc )));
response Time req uest->input resp onse Time )));
message req uest->input mes sage )));
trans Id req uest->input transId )));
error Cod e re quest ->in put errorCode )) ? :0;
extra D ata "";//custom data you passed initially now returned back unchanged
raw H ash ="partnerCode={ partnerCo de}&accessKey={ accessKe y}&requestId={ reques tI d}&amount={ amo unt}&orderId={ ord erI d}"."&orderInfo={ orde rIn fo}&responseTime={ res pon seT ime}{ ext raD ata}";
signatureFromMomo=req est >i nput sig nature );
ourSignature=ha sh hm ac sha256 rawH ash SECRET KEY );
if our Signature!= signature From Momo || errorC ode !=0){
log info ("Invalid MoMo webhook received".json encodereq est all()));
abort(400,"Bad Request");}
/* Process successful payment here */
return response json success truemessage OK ]200 headers Content Type application/json ];}
Zalo Pay退款流程实现
class ZalopayService{
const REFUND ENDPOINT https sb openapi zalopay vn /vp/refund ;
public static function process Refund(string zp Trans Token long refund Amount string description ){
timestamp round microtime true)*1000 );
params [
appid self APP ID,
zptransid zpTransToken ,
amoun t refundAmount ,
description substr description fifty ),
timestamp timestamp ];
Mac=self Compute Mac params );
params mac]=Mac;
response Http Helper post self REFUND ENDPOINT params );
result json decode response body true );
if isset result return code])&& result return code]==1){
/*
TODO:
Update your database to reflect refund status*/
return true;}else{
Log error ("ZaloPay refund failed".print r(resulttrue));
throw new Exception Failed to process refund with Zalo Pay.);}}
One Pay国际卡支付的3D Secure集成
对于需要3D认证的交易:
class ThreeDSecureHandler {
public static function initiateThreeDSecure(array card Details float amount string currency ){
one pay Client new One PayInternational();
auth Response one pay Client create Auth Request card Details amount currency three DSEnabled=>true]);
three Ds Method Url auth Response three ds method url ??null;
three Ds Server Trans ID auth Response three ds server trans id ??null;
session put current payment session [ ...cardDetails ,... authResponse ]);
redirect Url route checkout.threeds challenge name ]);
return view checkout.threeds initiate compact redirectUrl threeDsMethodUrl threeDsServerTransId amount currency cardDetails lastFour digits]));
}
public static function handle Challenge Completion(Request req ){
session Get current payment session ;
one pay Client new OnePayInternational();
finalize Result one payClient complete Three DS Auth session get threeds server trans id ],req all());
/* Verify MAC/signature etc.*/
switch finalize Result status]){
case succeeded :
Order Service mark Paid session get order id ]);
break;
default:
Order Service mark Failed(session get order id ]);
throw new PaymentFailedException();
}}}
Security Best Practices安全最佳实践
Essential Security Measures:
1.Always validate callbacks: Never trust incoming requests without verifying signatures.
2.Use HTTPS everywhere: Especially for callback URLs and when transmitting sensitive data.
3.Implement CSRF protection: On pages initiating payments.
4.Sanitize all inputs: Prevent XSS/SQL injection attacks.
5.Log important events: But don’t log full card details or sensitive tokens.
6.Store minimal data: Don’t store PAN/CVV after processing completes successfully.
7.Use up-to-date TLS versions(v1.2+).
8.Regularly rotate API keys/secrets especially after staff changes .
9.Monitor for suspicious activity like multiple failed attempts .
10.Keep PHP and dependencies patched against known vulnerabilities .
Troubleshooting Common Issues常见问题排查
Issue现象 | Possible Causes可能原因 | Solutions解决方案 |
---|---|---|
Invalid/Missing Signature签名错误 | Wrong hashing algorithm算法不一致 Parameter ordering issue参数顺序错误 Encoding problem编码问题 |
Double-check gateway docs文档确认 Ensure exact same parameter ordering严格按字母序 Test with their sample values使用测试数据验证 |
Callbacks not received未收到回调 | Firewall blocking防火墙拦截 URL incorrect地址错误 Temporary gateway outage服务暂时不可用 |
Whitelist gateway IPs白名单设置 Verify URL in merchant dashboard商户后台检查 Contact support联系支持团队 |
Payments stuck pending状态一直待处理 | Asynchronous processing延迟处理 Settlement batch timing结算批次时间差 Suspected fraud suspected欺诈审查中 Wait合理等待时间后查询交易状态或联系客服 |
Remember that each Vietnamese payment provider may have specific requirements so always refer to their latest official documentation before going live with any integration.