名著阅读 > 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践 > 17.2 公众号支付 >

17.2 公众号支付

公众号支付即JS API支付,是用户在微信中打开商户的Web页面,商户在Web页面通过调用微信支付提供的JS API接口调起微信支付模块完成支付。其应用场景有:

·用户在微信公众号内进入商家公众号,打开某个主页面,完成支付。

·用户的好友在朋友圈、聊天窗口等分享商家页面链接,用户点击链接打开商家页面,完成支付。

·将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面,完成支付。

微信支付的接口类代码如下。


  1 /**
  2 * JS API支付——H5网页端调起支付接口
  3 */
  4 class JsApi_pub extends Common_util_pub
  5 {
  6     var $code;                // Code码,用于获取OpenID
  7     var $openid;        // 用户的OpenID
  8     var $parameters;        // jsapi参数,格式为JSON
  9     var $prepay_id;        // 使用统一支付接口得到的预支付ID
 10     var $curl_timeout;// curl超时时间
 11 
 12     function __construct
 13     {
 14         // 设置curl超时时间
 15         $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
 16     }
 17 
 18     /**
 19      *     作用:生成可以获得Code的URL
 20      */
 21     function createOauthUrlForCode($redirectUrl)
 22     {
 23         $urlObj["appid"] = WxPayConf_pub::APPID;
 24         $urlObj["redirect_uri"] = urlencode($redirectUrl);
 25         $urlObj["response_type"] = "code";
 26         $urlObj["scope"] = "snsapi_base";
 27         $urlObj["state"] = "STATE"."#wechat_redirect";
 28         $bizString = $this->formatBizQueryParaMap($urlObj, false);
 29         return "https:// open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
 30     }
 31 
 32     /**
 33      *     作用:生成可以获得OpenID的URL
 34      */
 35     function createOauthUrlForOpenid
 36     {
 37         $urlObj["appid"] = WxPayConf_pub::APPID;
 38         $urlObj["secret"] = WxPayConf_pub::APPSECRET;
 39         $urlObj["code"] = $this->code;
 40         $urlObj["grant_type"] = "authorization_code";
 41         $bizString = $this->formatBizQueryParaMap($urlObj, false);
 42         return "https:// api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
 43     }
 44 
 45 
 46     /**
 47      *     作用:通过curl向微信提交Code,以获取OpenID
 48      */
 49     function getOpenid
 50     {
 51         $url = $this->createOauthUrlForOpenid;
 52         // 初始化curl
 53         $ch = curl_init;
 54         // 设置超时
 55         curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
 56         curl_setopt($ch, CURLOPT_URL, $url);
 57         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
 58         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE);
 59         curl_setopt($ch, CURLOPT_HEADER, FALSE);
 60         curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
 61         // 运行curl,结果以JSON形式返回
 62         $res = curl_exec($ch);
 63         curl_close($ch);
 64         // 取出OpenID
 65         $data = json_decode($res,true);
 66         $this->openid = $data['openid'];
 67         return $this->openid;
 68     }
 69 
 70     /**
 71      *     作用:设置prepay_id
 72      */
 73     function setPrepayId($prepayId)
 74     {
 75         $this->prepay_id = $prepayId;
 76     }
 77 
 78     /**
 79      *     作用:设置Code
 80      */
 81     function setCode($code_)
 82     {
 83         $this->code = $code_;
 84     }
 85 
 86     /**
 87      *     作用:设置jsapi的参数
 88      */
 89     public function getParameters
 90     {
 91         $jsApiObj["appId"] = WxPayConf_pub::APPID;
 92         $timeStamp = time;
 93         $jsApiObj["timeStamp"] = "$timeStamp";
 94         $jsApiObj["nonceStr"] = $this->createNoncestr;
 95         $jsApiObj["package"] = "prepay_id=$this->prepay_id";
 96         $jsApiObj["signType"] = "MD5";
 97         $jsApiObj["paySign"] = $this->getSign($jsApiObj);
 98         $this->parameters = json_encode($jsApiObj);
 99         return $this->parameters;
100     }
101 }

  

公众号支付的支付流程说明如下。

JS API支付前需要调用网页授权接口获取用户的OpenID。其实现的代码片段如下。


 1 // 使用JS API接口
 2 $jsApi = new JsApi_pub;
 3 
 4 // =========步骤1:网页授权获取用户的OpenID============
 5 // 通过Code获得OpenID
 6 if (!isset($_GET['code']))
 7 {
 8     // 触发微信返回Code码
 9     $url = $jsApi->createOauthUrlForCode(WxPayConf_pub::JS_API_CALL_URL);
10     Header("Location: $url"); 
11 }else
12 {
13     // 获取Code码,以获取OpenID
14     $code = $_GET['code'];
15     $jsApi->setCode($code);
16     $openid = $jsApi->getOpenId;
17 }
  

然后填充支付的参数,其中统一支付接口已经自动填充了以下参数的值。其代码如下。


$this->parameters["appid"] = WxPayConf_pub::APPID;                    // 公众账号ID
$this->parameters["mch_id"] = WxPayConf_pub::MCHID;                   // 商户号
$this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];      // 终端IP
$this->parameters["nonce_str"] = $this->createNoncestr;          // 随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);  // 签名
 

JS API支付再另外填写以下必要的参数,代码如下。


 1 // 统一支付接口中,trade_type为JSAPI时,OpenID为必填参数!
 2 $unifiedOrder->setParameter("openid","$openid");        // 商品描述
 3 $unifiedOrder->setParameter("body","方倍工作室");        // 商品描述
 4 // 自定义订单号,此处仅作举例
 5 $timeStamp = time;
 6 $out_trade_no = WxPayConf_pub::APPID."$timeStamp";
 7 $unifiedOrder->setParameter("out_trade_no","$out_trade_no");         // 商户订单号 
 8 $unifiedOrder->setParameter("total_fee","1");             // 总金额
 9 $unifiedOrder->setParameter("notify_url",WxPayConf_pub::NOTIFY_URL);  // 通知地址
10 $unifiedOrder->setParameter("trade_type","JSAPI");        // 交易类型
  

这些参数将生成一个如下的XML示例数据。


<xml>
    <openid><![CDATA[ou9dHt0L8qFLI1foP-kj5x1mDWsM]]></openid>
    <body><![CDATA[方倍工作室]]></body>
    <out_trade_no><![CDATA[wx88888888888888881414411779]]></out_trade_no>
    <total_fee>1</total_fee>
    <notify_url><![CDATA[http:// www.fangbei.org/wxpay/notify_url.php]]></notify_url>
    <trade_type><![CDATA[JSAPI]]></trade_type>
    <appid><![CDATA[wx8888888888888888]]></appid>
    <mch_id>10012345</mch_id>
    <spbill_create_ip><![CDATA[61.50.221.43]]></spbill_create_ip>
    <nonce_str><![CDATA[60uf9sh6nmppr9azveb2bn7arhy79izk]]></nonce_str>
    <sign><![CDATA[2D8A96553672D56BB2908CE4B0A23D0F]]></sign>
</xml>
  

将这些数据提交给统一支付接口后,返回如下XML数据。


<xml>
    <return_code><![CDATA[SUCCESS]]></return_code>  
    <return_msg><![CDATA[OK]]></return_msg>  
    <appid><![CDATA[wx8888888888888888]]></appid>  
    <mch_id><![CDATA[10012345]]></mch_id>  
    <nonce_str><![CDATA[Be8YX7gjCdtCT7cr]]></nonce_str>  
    <sign><![CDATA[885B6D84635AE6C020EF753A00C8EEDB]]></sign>  
    <result_code><![CDATA[SUCCESS]]></result_code>  
    <prepay_id><![CDATA[wx201410272009395522657a690389285100]]></prepay_id>  
    <trade_type><![CDATA[JSAPI]]></trade_type> 
</xml>
  

其中包含了最重要的预支付ID参数prepay_id。

前面的准备工作做好以后,JS API根据prepay_id生成jsapi支付参数,代码如下。


// =========步骤3:使用jsapi调起支付============
$jsApi->setPrepayId($prepay_id);
$jsApiParameters = $jsApi->getParameters;
  

上述代码生成的JSON参数如下。


{
    "appId": "wx8888888888888888",
    "timeStamp": "1414411784",
    "nonceStr": "gbwr71b5no6q6ne18c8up1u7l7he2y75",
    "package": "prepay_id=wx201410272009395522657a690389285100",
    "signType": "MD5",
    "paySign": "9C6747193720F851EB876299D59F6C7D"
}
  

最后在微信浏览器中使用上述参数调起微信支付,其代码如下。


<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    <title>微信安全支付</title>
    <script type="text/javascript">
        // 调用微信JS API支付
        function jsApiCall
        {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest',
                <?php echo $jsApiParameters; ?>,
                function(res){
                    WeixinJSBridge.log(res.err_msg);
                    // alert(res.err_code+res.err_desc+res.err_msg);
                }
            );
        }

        function callpay
        {
            if (typeof WeixinJSBridge == "undefined"){
                if( document.addEventListener ){
                    document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
                }else if (document.attachEvent){
                    document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
                    document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
                }
            }else{
                jsApiCall;
            }
        }
    </script>
</head>
<body>
    </br></br></br></br>
    <p >
        <button  type="button" 
        onclick="callpay" >贡献一下</button>
    </p>
</body>
</html>
  

当用户点击“贡献一下”按钮时,将弹出微信支付插件,用户就可以开始进行微信支付了。