首先需要登录微信商户中心申请,获取应用ID和支付密钥
然后做应用关联绑定 微信应用APPID
代码演示如下
$uid = intval($_GET['uid']);
$member = C::t(PT_USER)->get($uid);
if (!$member) {
json(['status' => 0, 'msg' => '用户UID不存在,请检查']);
}
$openid = $member['mp_openid'];
$amount = floatval($_GET['amount']);
$desc = data_filter($_GET['remark']);
$realname = data_filter($_GET['realname']);
$device_info = '';
include './class/WXBusiness.class.php';
$WXBusiness = new WXBusiness('你的商户ID', '你的支付密钥', '目标应用ID 如微信公众号');
$result = $WXBusiness->payToUser($openid, $amount, $desc, $realname, $device_info);
if (!$result) {
json(['status' => 0, 'msg' => $WXBusiness->getMsg()]);
}
$result = array_merge($result, $_POST);
C::t(PT_WXPAY_RECORD)->add($result);
json(['status' => 1, 'msg' => '付款成功']);
商户操作类如下 存放在 class目录下 WXBusiness.class.php
// 引入微信商户操作类
//include DISCUZ_ROOT . './class/WXBusiness.class.php';
// $WXBusiness = new WXBusiness();
class WXBusiness {
private $appid; //商户APPID
private $appserect;
protected $mchid;
protected $mchkey;
private $msg;
protected $errs = array(
'NO_AUTH' => '没有该接口权限',
'AMOUNT_LIMIT' => '金额超限',
'PARAM_ERROR' => '参数错误',
'OPENID_ERROR' => 'AOpenid错误',
'SEND_FAILED' => '付款错误,请查单确认付款结果,以查单结果为准',
'NOTENOUGH' => '余额不足',
'SYSTEMERROR' => '系统繁忙,请稍后再试。',
'NAME_MISMATCH' => '姓名校验出错',
'SIGN_ERROR' => '签名错误',
'XML_ERROR' => 'Post内容出错',
'FATAL_ERROR' => '两次请求参数不一致',
'FREQ_LIMIT' => '超过频率限制,请稍后再试。',
'MONEY_LIMIT' => '已经达到今日付款总额上限/已达到付款给此用户额度上限',
'CA_ERROR' => '商户API证书校验出错',
'V2_ACCOUNT_SIMPLE_BAN' => '无法给非实名用户付款',
'PARAM_IS_NOT_UTF8' => '请求参数中包含非utf8编码字符',
'SENDNUM_LIMIT' => '该用户今日付款次数超过限制,如有需要请登录微信支付商户平台更改API安全配置',
'RECV_ACCOUNT_NOT_ALLOWED' => '收款账户不在收款账户列表',
'PAY_CHANNEL_NOT_ALLOWED' => '本商户号未配置API发起能力',
);
function __construct($mchid, $mchkey, $appid, $appserect) {
$this->mchid = $mchid;
$this->mchkey = $mchkey;
$this->appid = $appid;
$this->appserect = $appserect;
}
public function payToUser($openid, $amount, $desc, $username = '', $device_info = '') {
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
$str = '<xml>
<mch_appid>%s</mch_appid>
<mchid>%s</mchid>
<device_info>%s</device_info>
<nonce_str>%s</nonce_str>
<sign>%s</sign>
<partner_trade_no>%s</partner_trade_no>
<openid>%s</openid>
<check_name>%s</check_name>
<re_user_name>%s</re_user_name>
<amount>%s</amount>
<desc>%s</desc>
<spbill_create_ip>%s</spbill_create_ip>
</xml>';
$params['mch_appid'] = $this->appid;
$params['mchid'] = $this->mchid;
$params['device_info'] = $device_info;
$params['nonce_str'] = random(32);//随机字符串32位
$params['partner_trade_no'] = md5(microtime(true) + random(15));
$params['openid'] = $openid;
//是否强制检测用户真实姓名
$params['check_name'] = $username ? 'FORCE_CHECK' : 'NO_CHECK';
$params['re_user_name'] = $username;
$params['amount'] = $amount * 100; //单位为分
$params['desc'] = $desc ?: '转账';
$params['spbill_create_ip'] = $_SERVER['SERVER_ADDR'];
ksort($params);
$string = $this->ToUrlParams($params);
$string .= "&key={$this->mchkey}";
$sign = strtoupper(md5($string));
$params['sign'] = $sign;
$data = sprintf(
$str,
$params['mch_appid'],
$params['mchid'],
$params['device_info'],
$params['nonce_str'],
$params['sign'],
$params['partner_trade_no'],
$params['openid'],
$params['check_name'],
$params['re_user_name'],
$params['amount'],
$params['desc'],
$params['spbill_create_ip']
);
//需要操作证书 往商户平台获取
$flag = $this->curl_post_ssl($url, $data, $msg,30, PERM_FILE_DIR);
if (!$flag) {
$this->msg = $msg;
return false;
}
$return = $this->xmltoArray($msg);
if (!$return) {
$this->msg = '无法获取退款转账信息';
return false;
}
if ($return['result_code'] != 'SUCCESS') {
$this->msg = $this->errs[$return['return_msg']]?:$return['return_msg'];
if ($return['err_code_des']) {
$this->msg=$this->msg.':'.$return['err_code_des'];
}
return false;
}
$params = array_merge($params,$return);
//记录返回的微信付款单号
// $params['payment_no'] = $return['payment_no'];
// $params['payment_time'] = $return['payment_time'];
return $params;
}
protected function xmltoArray($result) {
$return = simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA);
$return = json_encode($return);
$return = json_decode($return, true);
return $return;
}
protected function curl_post_ssl($url, $vars, &$msg = '', $second = 30, $fileDir = "C:/cert/", $aHeader = array()) {
$ch = curl_init();
//超时时间
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//以下两种方式需选择一种
//第一种方法,cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, $fileDir . 'apiclient_cert.pem');
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, $fileDir . 'apiclient_key.pem');
//第二种方式,两个文件合成一个.pem文件
// curl_setopt($ch, CURLOPT_SSLCERT, getcwd() . '/all.pem');
if (count($aHeader) >= 1) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
}
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
$data = curl_exec($ch);
if ($data) {
curl_close($ch);
$msg = $data;
return true;
} else {
$error = curl_errno($ch);
$msg = "call faild, errorCode:$error\n";
curl_close($ch);
return false;
}
}
// 获取消息内容
public function getMsg() {
return $this->msg;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams($data) {
$buff = "";
foreach ($data as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
}