微信支付——开发体系

有了微信支付方面的一些前期准备以及理论知识后,我们可以开始打造我们的微信支付平台了。

商户向微信公众号提供企业以及银行账户资料,商户功能审核通过后,可以获得以下账户包括财付通的商户账户,用于公众号支付

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的判断方法为:

<span style="white-space:pre">	</span>if(typeof WeixinJSBridge === 'undefined') {
		alert('微信支付必须在微信内置浏览器中使用.');
		return;
	}

微信提供getBrandWCPayRequest接口供商户前端网页调用,调用之前微信会鉴定商户支付权限,若商户具有调起支付的权限,则将开始支付流程。接口注意:所以传入参数都是字符串类型!   

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

支付失败

根据官方文档,将相关的函数和方法做了封装,代码如下:

<?php

/**
 * Description of weixinPayHelper
 *
 * @author kewin
 */

class weixinPayHelper {

	public $appId = "";
        public $appSecret = "";
        public $partnerId = "";
        public $paySignKey = "";
        public $partnerKey = "";
        public $token = "";

        public function __construct($appId, $appSecret, $partnerId, $paySignKey, $partnerKey, $token) {

                $this->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参数缺失!" . "<br>");
                 }
                 $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('获取微信公众号授权失败, 请稍后重试! 公众平台返回原始数据为: <br />' . $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;
        }

}

有了工具类后,就可以开始我们的支付流程过程需要的函数调用了, 附上一个微信支付的demo——pay.php

<?php
/**
 * [WeEngine System] Copyright (c) 2013 WE7.CC
 * kewin
 */
require '/www/web/weixin.shike001.com/source/bootstrap.inc.php';
include '../../source/class/weixinPayHelper.class.php';
取得公众号的一下数据$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);

?>

<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>微信支付</title>
<script type='text/javascript' src='../../resource/script/jquery-1.7.2.min.js'></script>
<style>
	.pay_table{
		border:1px solid #cad9ea;
		border-collapse: collapse;
		width:100%;
		font-size:15px;
	}
	.pay_table th{
		height:35px;
		background:#003399;
		color:#ffffff;
		font-size:16px;
	}
	.pay_table td{
		border:1px solid #aaa;
		height:30px;

	}

</style>
<script language="javascript">
function callpay()
{
	if(typeof WeixinJSBridge === 'undefined') {
		alert('微信支付必须在微信内置浏览器中使用.');
		return;
	}
	WeixinJSBridge.invoke('getBrandWCPayRequest',<?php echo $weixin->create_biz_json($package);?>,function(res){
	WeixinJSBridge.log(res.err_msg);

	if(res.err_msg == "get_brand_wcpay_request:ok"){
	    	//支付成功后的跳转
<span style="white-space:pre">		</span>window.location.href="";
	}else{
		//返回跳转到订单详情页面
		window.location.href="";

	}

	});
}
</script>
<body>
<div style="">
	<table class="pay_table">
		<th>订单支付</th>
		<tr>
			<td>订单名称:<?php echo $_GET['title']?></td>
		</tr>
		<tr>
			<td>支付金额:<?php echo sprintf('%.2f', $_GET['fee']);?><span class="muted"> 元</span></td>
		</tr>
	</table>
	<button type="button" style="width:100%;height:40px" onclick="callpay()">微信支付</button>
</div>

</body>
</html>

但此时要注意一个问题,微信的授权目录和请求路径已经固定,所以pay.php需要是请求调用的而不能是直接包含进某个功能块的,在这里我们可以跳转到支付页面,连接后面跟上需要的参数

至此,可以实现付款了。

时间: 2024-10-25 04:56:45

微信支付——开发体系的相关文章

微信公众平台开发(102) 微信支付账号体系

本文介绍微信支付账号体系各参数. 商户在微信公众平台提交申请资料以及银行账户资料,资料审核通过并签约后,可以获得表6-4所示帐户(包含财付通的相关支付资金账户),用于公众帐号支付. 帐号 作用 appId 公众帐号身份的唯一标识.审核通过后,在微信发送的邮件中查看. appSecret 公众帐号支付请求中用于加密的密钥Key,可验证商户唯一身份,PaySignKey对应于支付场景中的appKey值.审核通过后,在微信发送的邮件中查看. paySignKey 除了支付请求需要用到paySignKe

PHP微信支付开发实例

这篇文章主要为大家详细介绍了PHP微信支付开发过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 PHP微信支付开发过程,分享给大家,供大家参考,具体内容如下 1.开发环境 Thinkphp 3.2.3 微信:服务号,已认证 开发域名:http://test.paywechat.com (自定义的域名,外网不可访问) 2.需要相关文件和权限 微信支付需申请开通 微信公众平台开发者文档:http://mp.weixin.qq.com/wiki/home/index.html 微信支付开发者文档

微信支付开发 c# SDK JSAPI支付开发的流程和微信大坑

微信支付开发流程 1. 开通微信支付功能 省略 2. 下载微信的C#版的微信SDK 下载连接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 各个版本的都有,可惜咱最熟悉C# 我的下载时间是2016-02-28日,所以所有的流程都是基于这个版本的SDK. 3. 配置各种参数 首先在微信支付的开发配置中配置发起支付的路径如下: 代码的solution中找到lib的config.cs文件,如下图,所有的参数都在这里,这里配置

微信支付开发流程

授人以鱼不如授人以渔 微信支付开发流程 下面以PHP语言为例,对微信支付的开发流程进行一下说明. 1.获取订单信息 2.根据订单信息和支付相关的账号生成sign,并且生成支付参数 3.将支付参数信息POST到微信服务器,获取返回信息 4.根据返回信息生成相应的支付代码(微信内部)或是支付二维码(非微信内),完成支付. 下面分步骤的讲一下: 1.微信支付中相关的必须的订单参数有三个,分别是:body(商品名或订单描述),out_trade_no(一般为订单号)和total_fee(订单金额,单位“

微信支付开发(11) Native支付

关键字:微信公众平台 微信支付 Native原生支付作者:方倍工作室原文:http://www.cnblogs.com/txw1958/p/wxpay-native.html 在这篇微信公众平台开发教程中,我们将介绍如何开发实现微信支付的Native功能. 本文分为以下三个部分: 生成Native支付URL 生成二维码 生成Package 一.生成Native支付URL Native(原生)支付URL是一系列具有weixin://wxpay/bizpayurl?前缀的URL,同时后面紧跟着一系列

微信支付开发1 微信支付URL配置

本文介绍微信支付申请时如何设置授权目录及URL. 一.选择支付类型 目前有两种支付类型 JS API网页支付 Native原生支付 如果没有特殊要求,两种都勾选. 二.支付授权目录 目前可以选择http还是https协议,没有特别要求,选择http.对安全要求比较高的企业,请选择https. 支付授权目录填写自己公司的域名加上目录,目录可以是不存在的目录,例如wxpay. 一个完整的目录参考如下: http://www.doucube.com/wxpay/ 蓝色部分表示要选择的. 红色部分表示要

微信支付开发(8) 维权通知

本文介绍微信支付中如何获得维权通知. 一.维权通知URL 在 微信支付开发(1) 微信支付URL配置 已提到,维权通知URL为 http://www.doucube.com/wxpay/rights.php 二.用户维权系统接口 用户在公众号进行支付贩买行为出现异常时,通常会投诉到腾讯客服,因此微信侧需要即时了解公众号不用户交易的详情.同时,为了最快效率的解决用户的问题,微信作为连接用户和商户的桥梁,会通过此客服系统即时将问题同步给商户,并将解决结果反馈至用户. 用户在新增投诉单后,微信后台通知

微信支付开发(7) 告警通知

本文介绍微信支付中如何获得告警通知. 一.告警通知 为了及时通知商户异常,提高商户在微信平台的服务质量.微信后台会向商户推送告警通知,包括发货延迟.调用失败.通知失败等情况,通知的地址是商户在申请支付时填写的告警通知URL,在"公众平台-服务-服务中心-商户功能-商户基本资料-告警通知URL"可以查看.商户接收到告警通知后需要尽快修复其中提到的问题,以免影响线上经营. 告警通知URL接收的postData中还将含xml数据,格式如下: <xml> <AppId>

微信支付开发(6) 发货通知

本文介绍微信支付中发货通知功能的实现. 一.发货通知 为了更好地跟踪订单的情况,需要第三方在收到最终支付通知之后,调用发货通知API告知微信后台该订单的发货状态. 发货时间限制:虚拟.服务类24小时内,实物类72小时内. 请在收到支付通知后,按时发货,并使用发货通知接口将相关信息同步到微信后台.若平台在规定时间内没有收到,将视作发货超时处理. 发货通知API的URL为: https://api.weixin.qq.com/pay/delivernotify?access_token=xxxxxx