whmcs支付插件开发全教程

whmcs是一款售卖服务器的程序,国外很多卖vps的都在使用这套程序,只是改动多少的问题。当然用它来卖其他东西也是可以的,whmcs风格简洁但不少功能,也是不错的在线商城软件。

我们引入的是免签约插件——码支付,别怪我这么推荐它,主要是码支付真的太厚道了,别人付款直接到你的账户里,无需提现没有手续费,方便快捷,缺点就是需要一台挂软件的win电脑或服务器,每日有几百元的限额。

码支付这类的支付在whmcs里叫做第三方网关,首先定位到该系统根目录下modules/gateways,在gateways目录里存放着很多个支付文件,我们需要在该目录下新建一个文件:codepay_alipay,意为码支付的支付宝支付方式,下面开始正式whmcs支付开发之旅。如需帮助可以留言。

防止直接访问

因为支付文件的重要性,我们不允许用户能够直接访问该文件,所以我们要阻止所有通过不正当的途径访问该文件的方式。下面代码即为不是whmcs系统访问的全都显示错误信息并终止程序往下进行。

if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}

保存后你在浏览器访问本文件,将直接显示:This file cannot be accessed directly

配置插件信息

我们需要注明这个插件的版本号,作者名等信息。函数gatewaymodule_MetaData为whmcs系统内部定义的函数,我们可以直接拿来使用,gatewaymodule修改为文件名,例如我的文件为codepay_alipay.php,这个函数就叫做codepay_alipay_MtaData,必须同名,不然whmcs无法识别。

function codepay_alipay_MetaData()
{
return array(
'DisplayName' => 'codepay for whmcs',
'APIVersion' => '1.0', //版本信息
'DisableLocalCredtCardInput' => true,
'TokenisedStorage' => false,
);
}

配置插件设置页

接下来我们配置激活插件后,插件的设置页面,这里用户可以修改插件的配置信息,主要用来定义支付时显示的文字、码支付的id和密钥。

function codepay_alipay_config()
{
return array(
//支付时显示的文字
'FriendlyName' => array(
'Type' => 'System',
'Value' => '码支付—支付宝',
),
// 码支付id
'codepayId' => array(
'FriendlyName' => '码支付id',
'Type' => 'text',
'Size' => '25',
'Default' => '',
'Description' =>'输入你的码支付id',
),
// 码支付id
'codepayKey' => array(
'FriendlyName' => '码支付密钥',
'Type' => 'text',
'Size' => '25',
'Default' => '',
'Description' =>'输入你的码支付密钥',
),
);
}

设置支付网关

支付插件最为核心的部分,我们需要依靠这个函数来处理用户的付款,这个函数不对是无法付款成功的。

function codepay_alipay_link($params)
{
$parameter = array(
"id" => $params['codepayId'],
"type" => 1, //1为支付宝,2为qq钱包
"pay_id" => $params['invoiceid'], //账单号
"price" => $params['amount'], //这笔订单的付款
"outTime" => 300,
"page" => 1,
"return_url" => "",
"notify_url" => "",
);
//处理付款
$back = create_link($parameter, $params['codepayKey']);
return '<form method="post" action="'.$back.'">
<>本页账单已生成,请尽快支付。>
<input type="submit" value="' . $params['langpaynow'] . '" />
</form>';
}

这里我们把付款放到create_link函数里,就是让文件结构更清晰

//创建支付链接
function create_link($canshu, $codepay_key, $host="")
{
ksort($canshu); //重新排序$data数组
reset($canshu); //内部指针指向数组中的第一个元素
$sign = '';
$urls = '';
foreach ($canshu AS $key => $val) {
if ($val == '') continue;
if ($key != 'sign') {
if ($sign != '') {
$sign .= "&amp;";
$urls .= "&amp;";
}
$sign .= "$key=$val"; //拼接为url参数形式
$urls .= "$key=" . urlencode($val); //拼接为url参数形式
}
}

$key = md5($sign . $codepay_key);//开始加密
$query = $urls . '&amp;sign=' . $key; //创建订单所需的参数
$apiHost = ($host ? $host : "http://api2.fateqq.com:52888/creat_order/?"); //网关
$url = $apiHost . $query; //生成的地址
return $url;
}

官方还提供 gatewaymodule_refund 函数用于处理退款,但是谁希望用户买东西之后退款?所以不写,反正码支付API也不支持退款,需要退款可以手动操作,毕竟退款在少数。

异步通知

支付完成后,码支付网关会确认用户的付款信息并返回,我们需要获取码支付返回的信息,以此来判断用户是否付款成功并让whmcs做出处理。这一步也比较重要,不要让你用户付款后whmcs什么也不做,小心被认为骗钱把你举报了。

引入通知函数

require_once __DIR__ . '/../../../init.php';
require_once __DIR__ . '/../../../includes/gatewayfunctions.php';
require_once __DIR__ . '/../../../includes/invoicefunctions.php';

获取codepay_alipay的配置信息,比如码支付id和密钥

$gatewayParams = getGatewayVariables("codepay_alipay");

如果插件未激活,直接报错并退出。当然这段代码可以放在开头。

if (!$gatewayParams['type']) {
die("Module Not Activated");
}

处理码支付返回的数据以确认是否付款成功。码支付使用post方式返回数据。

function handleData($data)
{
$pay_id = $data['pay_id']; //需要充值的ID 或订单号 或用户名
$money = (float)$data['money']; //实际付款金额
$price = (float)$data['price']; //订单的原价
$type = (int)$data['type']; //支付方式
$pay_no = $data['pay_no']; //支付流水号
$param = $data['param']; //自定义参数 原封返回 您创建订单提交的自定义参数
$pay_time = (int)$data['pay_time']; //付款时间戳
$pay_tag = $data['tag']; //支付备注 仅支付宝才有 其他支付方式全为0或空
$status = 2; //业务处理状态 这里就全设置为2 如有必要区分是否业务同时处理了可以处理完再更新该字段为其他值
$creat_time = time(); //创建数据的时间戳

//判断支付金额、支付id和支付的时间,务必判断支付id存在,没有即为付款不成功!
if ($money &lt;= 0 || empty($pay_id) || $pay_time &lt;= 0 || empty($pay_no)) {
return "don't pay yet";
}else{
return "paid";
}
}

上面的是函数只是一个处理数据的模板,下面才是对实际传来的数据做出处理,并将处理后的数据套用这个模板就可以了。

$codepay_key = $gatewayParams["codepayKey"]; //这是您的密钥
$isPost = true; //默认为POST传入
if (empty($_POST)) { //如果GET访问
$_POST = $_GET; //POST访问 为服务器或软件异步通知 不需要返回HTML
$isPost = false; //标记为GET访问 需要返回HTML给用户
}
ksort($_POST); //排序post参数
reset($_POST); //内部指针指向数组中的第一个元素
$sign = ''; //加密字符串初始化
foreach ($_POST AS $key => $val) {
if ($val == '' || $key == 'sign') continue; //跳过这些不签名
if ($sign) $sign .= '&amp;'; //第一个字符串签名不加&amp; 其他加&amp;连接起来参数
$sign .= "$key=$val"; //拼接为url参数形式
}
$invoiceId = $_POST['pay_id']; //需要充值的ID 或订单号 或用户名
$amount = (float)$_POST['money']; //实际付款金额
$price = (float)$_POST['price']; //订单的原价
$param = $_POST['param']; //自定义参数
$type = (int)$_POST['type']; //支付方式
$transid = $_POST['pay_no'];//流水号

接下来套用模板,并做出判断:支付成功就确认订单生效;支付失败就不做处理。

if (!$_POST['pay_no'] || md5($sign . $codepay_key) != $_POST['sign']) { //不合法的数据
echo "invaild data";
} else { //数据合法
$result = handleData($_POST); //套用模板判断是否支付成功
if ($result == 'paid') { //确认支付成功
$invoiceId = checkCbInvoiceID( $invoiceId, $gatewayParams['name']);
//使订单、账单生效
checkCbTransID($transid);
addInvoicePayment($invoiceId,$transid,$amount,"0","codepay_alipay");
echo "ok";
} else {
$error_msg = defined('DEBUG') &amp;&amp; DEBUG ? $result : 'no'; //调试模式显示 否则输出no
if ($isPost) exit($error_msg); //服务器访问 返回给服务器
$result = "don't pay yet";
}
}

添加一个0.01元的商品,并自测付款,whmcs如果自动确认付款,码支付后台订单状态为“支付成功”即可。如有问题可以在码支付里调试,看看哪儿出错了。

另外whmcs新版不自动确认服务,需要手动审核,我测试可以使用钩子函数OrderPaid修改数据表达到自动审核的目的,不知道这个是不是通用做法。

whmcs的订单和账单是不一样的,订单为order,账单为invoice,订单id可以跟账单id不一样,后续开发中不要搞混了。下次有机会更新whmcs系统下的自动发卡插件。

纯干货没有图片,图文并茂不存在的。