年初时公司要在官网上添加一个支付功能“银联商务”支付。这个支付系统支持许多的银行在线支付。写在这里希望能帮助一些需要的朋友。
虽然ecshop的支付方式已经有好多了,但有时也有使用其它没有有支付系统。当然如果能看懂这个添加过程,那么其它的支付功能添加也不会太难。
与“银联商务”签合同就不说了。签合同后“银联商务”会给我们一个测试帐号与测试地址。 这是测试与调试必须要的。
还得到商户的控制台(用上面的地址帐号登录)下面的 "ChinaPay安装插件下载" 里找到下载 "NetPayClient php (通用版)安装程序" 。下载后里面有只一个PHP文件是处理整个加密密钥程序,必须要的,虽然代码进行了加密,但高手一看就可以解密出来。
一般我们可以不用解密出来,直接可以使用。如果没有解密那文件可以放在/includes/modules/ 下。不要放在/includes/modules/payment/ 下。
这个程序要用到PHP的“bcmath”高精度数学函数库,这个库在window里是自带的,但在linux里必须一般要安装才能使用。当然如果PHP已经安装了这个扩展就没有必要再装了。
linux系统下,给PHP安装扩展方法有很多,这时说下我使用的方法:
在终端输入命令 yum install php-bcmath 回车后 就可以等待安装完成。
下面来说下流程:
在/includes/modules/payment/ 下新建一个支付文件,命名可以自己定义的。如 chinaPay.php 这里就以chinaPay.php为名。
添加以下代码:
<?php
//银联商务 支付接囗
if (!defined('IN_ECS'))
{
die('Hacking attempt');
}
$payment_lang = ROOT_PATH . 'languages/' .$GLOBALS['_CFG']['lang']. '/payment/'.basename(__FILE__);
if (file_exists($payment_lang))
{
global $_LANG;
include_once($payment_lang);
}
/* 安装模块的基本信息后台管理中用到的 */
if (isset($set_modules) && $set_modules == TRUE)
{
$i = isset($modules) ? count($modules) : 0;
/* 代码 */
$modules[$i]['code'] = basename(__FILE__, '.php');
/* 描述对应的语言项 */
$modules[$i]['desc'] = 'chinaPay_desc';//对应语言文件中的说明内容下标
/* 是否货到付款 */
$modules[$i]['is_cod'] = '0';
/* 是否支持在线支付 */
$modules[$i]['is_online'] = '1';
/* 作者 */
$modules[$i]['author'] = 'XIHUAN';
/* 网址 */
$modules[$i]['website'] = 'http://www.ecshop.com';
/* 版本号 */
$modules[$i]['version'] = '1.0.0';
/* 配置信息 表单元素*/
$modules[$i]['config'] = array(
array('name' => 'chinaPay_MerId',/*对应语言文件中的下标*/ 'type' => 'text', 'value' => ''),//商户号
array('name' => 'chinaPay_MerPrk', /*对应语言文件中的下标*/ 'type' => 'text', 'value' => ''),//商户签名私钥地址
array('name' => 'chinaPay_PgPubk', /*对应语言文件中的下标*/ 'type' => 'text', 'value' => '')//ChinaPay签名私钥地址
);
return;
}
include ROOT_PATH.'/includes/modules/netpayclient.php'//如果解密了这个文件可以把代码定到这里就不用这句代码
/**
* 类
*/
class chinaPay{
function chinaPay()
{
}
function __construct()
{
$this->chinaPay();
}
//获取指定长度的字符串 不足以0填充
function str($str,$len,$fill='0')
{
if(strlen($str)<$len)
{
return str_repeat($fill, $len-strlen($str)).$str;
}
return substr($str, 0,$len);
}
//金额处理
function order_amount($amount)
{
if(strpos($amount, '.'))
return $this->str(str_replace('.', '', $amount), 12);
else return $this->str($amount.'00', 12);
}
/**
* 生成支付代码
* @param array $order 订单信息
* @param array $payment 支付方式信息
* 这里必须根据要求生成银联商务可用的连接代码
*/
function get_code($order, $payment)
{
//创建签名钥
if(empty($payment['chinaPay_MerId'])||empty($payment['chinaPay_MerPrk'])||!buildKey($payment['chinaPay_MerPrk']))return '<p>支付出错!请联系园田居!</p>';
$order_no=$this->str($order['order_sn'], 16);//处理定单
$TransAmt=$this->order_amount($order['order_amount']);//订单交易金额
$Priv1='chinaPay';
$CuryId='156';//订单交易币种
$TransDate=date('Ymd');//订单交易日期
$TransType='0001';//交易类型
$msg=$payment['chinaPay_MerId'].$order_no.$TransAmt.$CuryId.$TransDate.$TransType.$Priv1;
$button='<form action="http://payment-test.chinapay.com/pay/TransGet" METHOD="POST" target="_blank">'.// (这里action的内容为提交交易数据的URL地址)
'<input type="hidden" name="MerId" value="'.$payment['chinaPay_MerId'].'"/>'.// (MerId为ChinaPay统一分配给商户的商户号,15位长度,必填)
'<input type="hidden" name="OrdId" value="'.$order_no.'"/>'. //(商户提交给ChinaPay的交易订单号,16位长度,必填)
'<input type="hidden" name="TransAmt" value="'.$TransAmt.'"/>'. //(订单交易金额,12位长度,左补0, 必填,单位为分)
'<input type="hidden" name="CuryId" value="'.$CuryId.'"/>'.// (订单交易币种,3位长度,固定为人民币156, 必填)
'<input type="hidden" name="TransDate" value="'.$TransDate.'"/>'.//(订单交易日期,8位长度,必填)
'<input type="hidden" name="TransType" value="0001"/>'.// (交易类型,4位长度,必填)
'<input type="hidden" name="Version" value="20070129"/>'.// (支付接入版本号,必填)
'<input type="hidden" name="BgRetUrl" value="'.$GLOBALS['ecs']->url() . 'respond.php'.'"/>'.// (后台交易接收URL,长度不要超过80个字节,必填)
'<input type="hidden" name="PageRetUrl" value="'.$GLOBALS['ecs']->url() . 'respond.php'.'"/>'.// (页面交易接收URL,长度不要超过80个字节,必填)
'<input type="hidden" name="GateId" value=""/>'.//(支付网关号,可选)
'<input type="hidden" name="Priv1" value="'.$Priv1.'"/>'.//(商户私有域,长度不要超过60个字节) 传参数用的
'<input type="hidden" name="ChkValue" value="'.sign($msg).'"/>'.//(256字节长的ASCII码,为此次交易提交关键数 据的数字签名,必填)
'<input type="submit" value="立即使用银联支付"/>
</form>';
return $button;
}
/**
* 响应操作
这里主要处理银联回传的数据 并且要处理付款的形式 是否为付款成功 或付款中 这里的状态很重要
回传的数据 验证最重要的
*/
function respond()
{
/*
当消费支付交易完成时,ChinaPay会将交易应答信息发送给商户,
对于页面易接收地址和后台交易接收地址都会收到交易接收数据,
应答的数据域段包括如下内容:(以页面Form数据为例,注意大小写,
后台应答数据的发送的域段名和下面的一致)
<form name="SendToMer" method="post" action=""> (这里action的内容为提交交易数据的URL地址)
<input type="hidden" name="merid" value="808000000000001"/>(MerId为ChinaPay统一分配给商户的商户号,15位长度,必填)
<input type="hidden" name="orderno" value="0000000010096806"/> (商户提交给ChinaPay的交易订单号,16位长度,必填)
<input type="hidden" name="transdate" value="20070801"/> (订单交易日期,8位长度,必填)
<input type="hidden" name="amount" value="000000001234"/> (订单交易金额,12位长度,左补0, 必填,单位为分)
<input type="hidden" name="currencycode" value="156"/>(订单交易币种,3位长度,固定为人民币156, 必填)
<input type="hidden" name="transtype" value="0001"/> (交易类型,4位长度,必填)
<input type="hidden" name="status" value="1001"/> (交易状态,4位长度,必填)
<input type="hidden" name="checkvalue" value=" X…X "/> (256字节长的ASCII码,为此次交易提交关键数 据的数字签名,必填)
<input type="hidden" name="GateId" value=" 0001"/> (支付网关号,可选)
<input type="hidden" name="Priv1" value=" Memo"/> (商户私有域,长度不要超过60个字节) 传参数用的
</form>
status 表示交易转态,只有"1001"的时候才为交易成功,其他均为失败,因此在验证签名数据为ChinaPay发出的以后,还需要判定交易状态代码为"1001"。
*/
$sql = "SELECT pay_config FROM " . $GLOBALS['ecs'] ->table('payment') . " WHERE pay_code = 'chinaPay' AND enabled = '1'";
$pay = $GLOBALS['db']->getRow($sql);//取出内容
if(empty($pay['pay_config']))return false;//支付配置数据不存在
$payment = unserialize($pay['pay_config']);
foreach ($payment as $value)
{
if($value['name']=='chinaPay_PgPubk')
{
$PgPubk=$value['value'];
break;
}
}
//创建验证钥
if(!isset($PgPubk)||!buildKey($PgPubk))return false;
if (empty($_POST)||!is_array($_POST))return false;//交易失败
$respond=array('merid','orderno','transdate','amount','currencycode','transtype','status','checkvalue','GateId');
foreach ($respond as $val)
{
if(array_key_exists($val, $_POST));else return false;//交易失败返回数据不合法或缺少
}
/* 检查支付的金额是否相符 */
$order_no=substr($_POST['orderno'], -13);//取出定单号这块可以不去处理SQL注入,后面调用的函数都会判断这个变量的值是否为全数字
$log_id=get_order_id_by_sn($order_no);//取出支付编号
//判断是为为充值定单
if(empty($log_id))$log_id=get_order_id_by_sn($order_no,true);//取出支付编号
if (!check_money($log_id, (float)substr_replace($_POST['amount'], '.', -2,0)))
{
return false;
}
//判断交易状态
if($_POST['status']!='1001')return false;//交易失败
/* 检查数字签名是否正确 */
//验证交易应答
if(!verifyTransResponse($_POST['merid'], $_POST['orderno'], $_POST['amount'], $_POST['currencycode'], $_POST['transdate'], $_POST['transtype'], $_POST['status'], $_POST['checkvalue']))
return false;//交易失败
/* 改变订单状态 */
order_paid($log_id);
return true;//支付成功
}
}
?>
以上是整个前端处理核心代码。
然后这个可能建立一个语言文件
文件名与支付文件名一样。如果有多个语言可以在/languages/en_us/payment/ ;/languages/zh_cn/payment/ ;/languages/zh_wt/payment/ 下都建立一个同样的文件(只要处理好显示的内容为不同的语言就可以)。
添加以下代码:
<?php
//银联商务 支付接囗
global $_LANG;
$_LANG['chinaPay'] = '银联商务';
$_LANG['chinaPay_desc'] = '银联商务网站(http://www.chinaums.com/) 是国内先进的网上支付平台。无预付/年费,单笔费率0.6%-0.8%,无流量限制。<br/><a href="http://www.chinaums.com/" target="_blank"><font color="red">立即在线申请</font></a>';
$_LANG['chinaPay_MerId'] = '商户号';
$_LANG['chinaPay_MerPrk'] = '商户签名私钥地址';
$_LANG['chinaPay_PgPubk'] = 'ChinaPay签名私钥地址';
$_LANG['pay_button'] = '立即使用银联商务支付';
?>
到这里客户用使用代码基本写完。后面还得添加后台管理内容。
把测试用的私钥两个文件上传到要测试的服务器中,尽量不要放在服务器的目录下。
进入 系统后台 -> 系统设置 -> 支付方式 菜单就可以看到多了一个支付方式,“银联商务”。
在“银联商务”中可以看到最后面一列中有个“安装”链接,点击安装。
在“商户号”,“商户签名私钥地址”,“ChinaPay签名私钥地址” 中填入测试用的帐户与两个私钥地址。注意一个是公钥(ChinaPay签名私钥地址)一个是私钥(商户签名私钥地址)。再“确定” 到这里安装基本完成。
剩下的就是测试了。进入网店下单走到支付确认页面中就可以看到多了一个支付选项。选择“银联商务”支付。确认后,会进入跳转支付平台的页面。点“立即使用银联商务支付”就可以进入测试的“银联商务”平台,输入测试用的银行卡号与密码,再提交。测试平台会返回一个测试结果,如果成功等待几秒后会返回到我们的网店确认支付页面。只要这个页面提示支付成功,整个安装过程完成。
最后是待正式上线时记得修改“商户号”,“商户签名私钥地址”,“ChinaPay签名私钥地址”为正式的内容。