有了微信支付方面的一些前期准备以及理论知识后,我们可以开始打造我们的微信支付平台了。
商户向微信公众号提供企业以及银行账户资料,商户功能审核通过后,可以获得以下账户包括财付通的商户账户,用于公众号支付
appId
公众号身份的唯一标识。
paySignKey
公众号支付请求中用于加密的密钥Key,可验证商户唯一身份,PaySignKey对应于支付场景中的appKey值。
appSecret
除了支付请求需要用到paySignKey,公众平台接口API 的权限获取所需密钥Key,在使用所有公众平台API 时,都需要先用它去换取access_token,然后再进行调用(详情参考文档API 接口部分)。
partnerId
财付通商户身份的标识。
partnerKey
财付通商户权限密钥Key。
其中:appSecret、paySignKey、partnerKey是验证商户唯一性的安全标识,对于appSecret和paySignKey的区别,可以这样认为:appSecret是API使用时的登录密码,会在网络中传播的,而paySignKey是在所有支付相关数据传输时用于加密并进行身份校验的密钥,仅保留在第三方后台和微信后台,不会在网络中传播,而且paySignKey仅用于支付请求。
JS API(网页内)支付接口
根据支付场景的交互细节,来设计商户页面的逻辑
(1)用户打开商户网页选购商品,发起支付,在网页通过javascript调用getBrandWCPayRequest接口,发起微信支付请求,用户进入支付流程。
(2)用户成功支付点击完成按钮后,商户的前端会收到javascript的返回值。商户直接跳转支付成功的静态页面进行展示。
(3)商户后台收到来自微信开放平台的支付成功回调通知,标志该笔订单支付成功。
微信JS API只能在微信内置浏览器中使用,其他浏览器调用无效。js的判断方法为:
if(typeof WeixinJSBridge === 'undefined') {
alert('微信支付必须在微信内置浏览器中使用.');
return;
}
getBrandWCPayRequest参数以及返回值定义
参数 |
名称 |
必填 |
格式 |
说明 |
appId |
公众号id |
是 |
字符串类型 |
商户注册具有支付权限的公众号成功后即可获得; |
timeStamp |
时间戳 |
是 |
字符串类型,32个字节以下 |
商户生成,从1970年1月1日00:00:00至今的秒数,即当前的时间,且最终需要转换为字符串形式; |
nonceStr |
随机字符串 |
是 |
字符串类型,32个字节以下 |
商户生成的随机字符串; |
package |
订单详情扩展字符串 |
是 |
字符串类型,4096个字节以下 |
商户将订单信息组成该字符串,具体组成方案参见接口使用说明中package组包帮劣;由商户按照规范拼接后传入; |
signType |
签名方式 |
是 |
字符串类型,参数取值"SHA1" |
按照文档中所示填入,目前仅支持SHA1; |
paySign |
签名 |
是 |
字符串类型 |
商户将接口列表中的参数按照指定方式迚行签名,签名方式使用signType中标示的签名方式,具体签名方案参见接口使用说明中签名帮劣;由商户按照规范签名后传入; |
返回值是:
err_msg |
get_brand_wcpay_request:ok |
支付成功 |
get_brand_wcpay_request:cancel |
支付过程用户取消 |
|
get_brand_wcpay_request:fail |
支付失败 |
根据官方文档,将相关的函数和方法做了封装,代码如下:
appId = $appId;
$this->appSecret = $appSecret;
$this->partnerId = $partnerId;
$this->paySignKey = $paySignKey;
$this->partnerKey = $partnerKey;
$this->token = $token;
}
/**
*
* @param type $api_url
* @return boolean
*/
public function open_request($api_url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
}
$ret = curl_exec($ch);
$error = curl_errno($ch);
curl_close($ch);
if ($error) {
return false;
}
return $ret;
}
/**
* 对数组排序
* @param type $para 排序前的数组
* @return type 排序后的数组
*/
public function argsort($para) {
ksort($para);
reset($para);
return $para;
}
/**
* 把数组所有元素按照"参数=参数值"的模式用&字符拼接成字符串
* @param type $para
* @return type
*/
public function createlinkstring($para) {
$args = "";
foreach ($para as $key => $val) {
$args .= strtolower($key) . "=" . $val . "&";
}
//去掉最后一个&字符
if (strlen($args) > 0) {
$args = substr($args, 0, count($args) - 2);
}
//如果存在转义字符,那么去掉转义
if (get_magic_quotes_gpc()) {
$args = stripcslashes($args);
}
return $args;
}
/**
* 除去数组中的空值和签名参数
* @param type $para
* @return type
*/
public function parafilter($para) {
$para_filter = array();
foreach ($para as $key => $val) {
if ($key == "sign_method" || $key == "sign" || $val == "") {
continue;
} else {
$para_filter[$key] = $para[$key];
}
}
return $para_filter;
}
/**
* 创建sign
* @param type $arr
* @return type
*/
public function create_sign($arr) {
$para = $this->parafilter($arr);
$_signValue = $this->argsort($para);
$signValue_ = $_signValue . "&key=" . $this->partnerKey;
$signValue = strtoupper(md5($signValue_));
return $signValue;
}
/**
* 创建app_signature
* @param type $arr
* @return type
*/
public function create_app_signature($arr) {
$_para = $this->parafilter($arr);
$para = $this->argsort($_para);
$signValue = sha1($this->createlinkstring($para));
return $signValue;
}
/**
*
* 生成jsapi支付请求json
*/
public function create_biz_json($package){
if($this->check_cft_parameters($package) == false) {
message("生成package参数缺失!" . "
");
}
$nativeObj["appId"] = $this->appId;
$nativeObj["package"] = $package;
$nativeObj["timeStamp"] = "".time()."";
$nativeObj["nonceStr"] = $this->create_noncestr();
$nativeObj["paySign"] = $this->getSign($nativeObj);
$nativeObj["signType"] = 'SHA1';
return json_encode($nativeObj);
}
/**
* 获取paySign
* @param type $package
*/
public function getSign($bizObj){
foreach ($bizObj as $k => $v){
$bizParameters[strtolower($k)] = $v;
}
$bizParameters["appkey"] = $this -> paySignKey;
ksort($bizParameters);
$bizString = $this->formatBizQueryParaMap($bizParameters, false);
return sha1($bizString);
}
function check_cft_parameters($package){
if($package["bank_type"] == null || $package["body"] == null || $package["partner"] == null ||
$package["out_trade_no"] == null || $package["total_fee"] == null || $package["fee_type"] == null ||
$package["notify_url"] == null || $package["spbill_create_ip"] == null || $package["input_charset"] == null
)
{
return false;
}
return true;
}
function getToken($weid) {
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
$content = ihttp_get($url);
if (empty($content) || !is_array($content)) {
message('获取微信公众号授权失败, 请稍后重试!');
}
$token = @json_decode($content['content'], true);
if (empty($token) || !is_array($token)) {
message('获取微信公众号授权失败, 请稍后重试! 公众平台返回原始数据为:
' . $content['meta']);
}
if (empty($token['access_token']) || empty($token['expires_in'])) {
message('解析微信公众号授权失败, 请稍后重试!');
}
$record = array();
$record['token'] = $token['access_token'];
$record['expire'] = TIMESTAMP + $token['expires_in'];
$row = array();
$row['access_token'] = iserializer($record);
pdo_update('wechats', $row, array('weid' => $weid));
return $record['token'];
}
/**
* 发货通知
* @param type $openid
* @param type $transid 交易单号
* @param type $out_trade_no 商户订单号
* @param type $deliver_status
* @param type $deliver_msg
* @return type
*/
public function delivernotify($weid,$openid, $transid, $out_trade_no, $deliver_status = '1', $deliver_msg = 'ok') {
$post = array();
$post['appid'] = $this->appId;
$post['appkey'] = $this->paySignKey;
$post['openid'] = $openid;
$post['transid'] = $transid;
$post['out_trade_no'] = $out_trade_no;
$post['deliver_timestamp'] = time();
$post['deliver_status'] = $deliver_status;
$post['deliver_msg'] = $deliver_msg;
ksort($post);
$bizString = $this -> formatBizQueryParaMap($post, false);
$post['app_signature'] = sha1($bizString);
$post['sign_method'] = "SHA1";
$jsonmenu =
'{
"appid" : "'.$post['appid'].'",
"openid" : "'.$post['openid'].'",
"transid" : "'. $post['transid'].'",
"out_trade_no" : "'.$post['out_trade_no'].'",
"deliver_timestamp" : "'.$post['deliver_timestamp'].'",
"deliver_status" : $deliver_status,
"deliver_msg" : $deliver_msg,
"app_signature" : "'.$post['app_signature'].'",
"sign_method" : "sha1"
}';
$url = "https://api.weixin.qq.com/pay/delivernotify?access_token=".$this->token;
$ret = $this->open_request($url, $jsonmenu);
if ( in_array($ret['errcode'],array(40001,40002,42001))){
$this->token = $this->getToken($weid);
return $this->delivernotify($weid,$openid,$transid,$out_trade_no,$deliver_status,$deliver_msg);
}
return $ret;
}
/**
* 订单查询
* @param type $out_trade_no
* @return type
*/
public function order_query($out_trade_no) {
$post = array();
$post['appid'] = $this->appId;
$sign = $this->create_sign(array('out_trade_no' => $out_trade_no, 'partner' => $this->partnerId));
$post['package'] = "out_trade_no=$out_trade_no&partner=" . $this->partnerId . "&sign=$sign";
$post['timestamp'] = time();
$post['app_signature'] = $this->create_app_signature(array('appid' => $this->appId, 'appkey' => $this->paySignKey, 'package' => $post['package'], 'timestamp' => $post['timestamp']));
$post['sign_method'] = "SHA1";
$data = json_encode($post);
$url = 'https://api.weixin.qq.com/pay/orderquery?access_token=' . $this->token;
$ret = $this->open_request($url, $data);
return $ret;
}
/**
* 构建支付请求数组
* @param type $parameter
* @return type
*/
public function buildForm($parameter) {
$parameter['package'] = $this->buildPackage($parameter); //生成订单package
$paySignArray = array('appid' => $this->appId, 'appkey' => $this->paySignKey, 'noncestr' => $parameter['noncestr'], 'package' => $parameter['package'], 'timestamp' => $parameter['timestamp']);
$parameter['paysign'] = $this->create_app_signature($paySignArray);
return $parameter;
}
function formatBizQueryParaMap($paraMap, $urlencode){
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v){
if($urlencode){
$v = urlencode($v);
}
$buff .= strtolower($k) . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
function formatQueryParaMap($paraMap, $urlencode){
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v && "sign" != $k) {
if ($urlencode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar;
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
/**
* 构建支付请求包
* @param type $parameter
* @return type
*/
public function buildPackage($parameter) {
$filter = array('bank_type', 'body', 'attach', 'partner', 'out_trade_no', 'total_fee', 'fee_type', 'notify_url', 'spbill_create_ip', 'time_start', 'time_expire', 'transport_fee', 'product_fee', 'goods_tag', 'input_charset');
$base = array(
'bank_type' => 'WX',
'fee_type' => '1',
'input_charset' => 'UTF-8',
'partner' => $this->partnerId,
'noncestr' => md5(uniqid(time(), true))
);
$parameter = array_merge($parameter, $base);
$unSignParaString = $this->formatQueryParaMap($parameter, false);
$paraString = $this->formatQueryParaMap($parameter, true);
$string = $unSignParaString."&key=" . $this->partnerKey;
$sign = strtoupper(md5($string));
return $paraString . "&sign=" . $sign;
}
/**
* 从xml中获取数组 post过来的数据
* @return boolean
*/
public function getXmlArray() {
$postStr = file_get_contents('php://input');
if ($postStr) {
$postStr = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
if (!is_object($postStr)) {
return false;
}
$array = json_decode(json_encode($postStr), true); //xml对象转数组
return array_change_key_case($array, CASE_LOWER); //所有键小写
} else {
return false;
}
}
/**
* 创建随机串
* @param type $length
* @return type
*/
public function create_noncestr($length = 16) {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str.= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
public function trimString($value) {
$ret = null;
if (null != $value) {
$ret = $value;
if (strlen($ret) == 0) {
$ret = null;
}
}
return $ret;
}
}
取得公众号的一下数据$appid $secret $partnerId $paySignKey $partnerKey $token
$weixin = new weixinPayHelper($appid, $secret, $partnerId, $paySignKey, $partnerKey, $token);
$body = $_GET['title'];
$fee = $_GET['fee']*100;
$create_ip = $_W['clientip'];
$data = array(
'body' => $body,
'spbill_create_ip' => $create_ip,
'total_fee' => "$fee",
'attach' => '120',
'notify_url' => $_W['siteroot'] . 'notify2.php',
'out_trade_no' => $weixin->create_noncestr(),
);
$package = $weixin->buildPackage($data);
?>
微信支付
订单支付
订单名称:
支付金额: 元
至此,可以实现付款了。