流程:
微信用戶從微信公衆號進入查詢頁面(網頁授權獲取openid),進行提交申請單。
後臺管理員進行審覈(支付,駁回),主要記錄支付操作。
事先準備:
1、商戶號
2、公衆號(開通支付功能)
3、商戶號綁定到公衆號
4、證書(由商戶號進行生成導出,用於api驗證)https://kf.qq.com/faq/180824BrQnQB180824m6v2yA.html
主要操作:主要兩個動作(獲取openid,付款)
一、 獲取用戶openid,並記錄。(時間,申請金額什麼的亂七八糟的參數略略略略)
可由公衆號會話生成(未用)
網頁授權生成(因爲有頁面用此方法)
由中轉頁獲取code並請求獲取用戶openid。
jump.php
<?php
$url = "http:**************/jump.php";
baseAuth($url);
/**
* 獲取用戶的openid
* @param string $openid [description]
* @return [type] [description]
*/
function baseAuth($redirect_url)
{
//1.準備scope爲snsapi_base網頁授權頁面
$baseurl = urlencode($redirect_url);
$snsapi_base_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=w***ad&redirect_uri=' . $baseurl . '&response_type=code&scope=snsapi_base&state=YQJ#wechat_redirect';
//2.靜默授權,獲取code
//頁面跳轉至redirect_uri/?code=CODE&state=STATE
$code = $_GET['code'];
if (!isset($code)) {
header('Location:' . $snsapi_base_url);
}
//3.通過code換取網頁授權access_token和openid
$curl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=w*****&secret=0*****18&code=' . $code . '&grant_type=authorization_code';
$content = curl($curl);
$result = json_decode($content, true);
header("Location:http*******alance.php?open_id={$result['openid']}");//最終頁面
}
//設置網絡請求配置
function curl($curl, $https = true, $method = 'GET', $data = null)
{
// 創建一個新cURL資源
$ch = curl_init();
// 設置URL和相應的選項
curl_setopt($ch, CURLOPT_URL, $curl); //要訪問的網站
curl_setopt($ch, CURLOPT_HEADER, false); //啓用時會將頭文件的信息作爲數據流輸出。
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //將curl_exec()獲取的信息以字符串返回,而不是直接輸出。
if ($https) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //FALSE 禁止 cURL 驗證對等證書(peer's certificate)。
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); //驗證主機
}
if ($method == 'POST') {
curl_setopt($ch, CURLOPT_POST, true); //發送 POST 請求
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); //全部數據使用HTTP協議中的 "POST" 操作來發送。
}
// 抓取URL並把它傳遞給瀏覽器
$content = curl_exec($ch);
if ($content === false) {
return "網絡請求出錯: " . curl_error($ch);
exit();
}
//關閉cURL資源,並且釋放系統資源
curl_close($ch);
return $content;
}
二、 付款(用到之前準備項)
官方:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
1、請求參數(簡化過了,針對不同進行需求修改)
//支付rpc
$this->wx_pay_client = new xmlrpc_client('http:/*********_server.php', 'app_com');
....
$pay_id = time() . rand(1000, 9999);
$money = $log_data['money'];
$arr = array(
'appid' => 'w*****d',//公衆號appid
'partner_trade_no' => $pay_id,//商戶訂單號(標準見官方文檔)
'openid' => $open_id,//接收人openid
'amount' => $money * 100,//單位分
'desc' => '微信提現',//描述
);
$rs = $this->wx_pay_client->call('transfers', $arr);//封裝過的支付rpc
if ($rs['result_code'] != 'SUCCESS') {
$arr = array(
'ret' => 0,
'msg' => $rs['return_msg']
);
}else{
}
****_server.php(rpc,封裝了一些常用功能)
<?php
define('APPID',"****c");//默認appid(選填)
define('MCHID',"****");//商戶號
define('PARTNERKEY',"***********2");//商戶key
include_once("***class_xmlrpc.php");//xmlrpc操作類
class mmpaymktTransfers {
//核心支付函數,參數:請求地址和參數
function postSSLCurl($url,$obj) {
$obj['nonce_str'] = $this->create_noncestr(); //創建隨機字符串
$obj['sign'] = $this->getSign($obj,false); //將簽名傳入數組
$postXml = $this->arrayToXml($obj); //將參數轉爲xml格式
$responseXml = $this->curl_post_ssl($url,$postXml); //提交請求
$resarr = $this->xmlToArray($responseXml);
return $resarr;
}
//生成簽名,參數:生成簽名的參數和是否編碼
function getSign($arr,$urlencode) {
$buff = "";
ksort($arr); //對傳進來的數組參數裏面的內容按照字母順序排序,a在前面,z在最後(字典序)
foreach ($arr as $k=>$v) {
if(null!=$v && "null" != $v && "sign" != $k) { //簽名不要轉碼
if ($urlencode) {
$v = urlencode($v);
}
$buff.=$k."=".$v."&";
}
}
if (strlen($buff)>0) {
$reqPar = substr($buff,0,strlen($buff)-1); //去掉末尾符號“&”
}
$stringSignTemp = $reqPar."&key=".PARTNERKEY; //簽名後加api
$sign = strtoupper(md5($stringSignTemp));
return $sign;
}
//生成隨機字符串,默認32位
function create_noncestr($length=32) {
//創建隨機字符
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for($i=0;$i<$length;$i++) {
$str.=substr($chars, mt_rand(0,strlen($chars)-1),1);
}
return $str;
}
//數組轉xml
function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key=>$val) {
if (is_numeric($val)) {
$xml.="<".$key.">".$val."</".$key.">";
} else {
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
//xml轉數組
function xmlToArray($string)
{
$ob= simplexml_load_string($string,'SimpleXMLElement', LIBXML_NOCDATA);//將字符串轉化爲變量
$json = json_encode($ob);//將對象轉化爲JSON格式的字符串
$array = json_decode($json, true);
return $array;
}
//post請求網站,需要證書
function curl_post_ssl($url, $vars, $second=30,$aHeader=array())
{
$ch = curl_init();
//超時時間
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
//這裏設置代理,如果有的話
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
//cert 與 key 分別屬於兩個.pem文件(關鍵點!!)
//請確保您的libcurl版本是否支持雙向認證,版本高於7.20.1
curl_setopt($ch,CURLOPT_SSLCERT,dirname(dirname(__FILE__)).'/weixin/cert/apiclient_cert.pem');
curl_setopt($ch,CURLOPT_SSLKEY,dirname(dirname(__FILE__)).'/weixin/cert/apiclient_key.pem');
curl_setopt($ch,CURLOPT_CAINFO,dirname(dirname(__FILE__)).'/weixin/cert/rootca.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);
return $data;
}
else {
$error = curl_errno($ch);
echo "call faild, errorCode:$error\n";
curl_close($ch);
return false;
}
}
}
//發送紅包
function sendredpack($m,$p)
{
$arr = $p[0];
$obj = array();
$obj['wxappid'] = APPID;//appid
$obj['mch_id'] = MCHID;//mch_id
$obj['mch_billno'] = $arr['mch_billno'];//訂單號,自定義
$obj['client_ip'] = $_SERVER['REMOTE_ADDR'];
$obj['re_openid'] = $arr['openid'];//接收紅包openid
$obj['total_amount'] = $arr['money'];
$obj['min_value'] = $arr['money'];
$obj['max_value'] = $arr['money'];
$obj['total_num'] = 1;
$obj['nick_name'] = $arr['sender'];
$obj['send_name'] = $arr['sender'];
$obj['wishing'] = $arr['wishing'];
$obj['act_name'] = $arr['act_name'];
$obj['remark'] = "";
$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
$mmpaymktTransfers = new mmpaymktTransfers();
$resarr = $mmpaymktTransfers->postSSLCurl($url, $obj);
return $resarr;
}
//查詢紅包領取狀態
function gethbinfo($m,$p)
{
$mch_billno = $p[0];
$obj['mch_id'] = MCHID;//mch_id
$obj['appid'] = APPID;//appid
$obj['mch_billno'] = $mch_billno;
$obj['bill_type'] = 'MCHT';
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo';
$mmpaymktTransfers = new mmpaymktTransfers();
$resarr = $mmpaymktTransfers->postSSLCurl($url, $obj);
return $resarr;
}
//付款至錢包
function transfers($m,$p)
{
$arr = $p[0];
$obj = array();
$obj['mch_appid'] = $arr['appid']?$arr['appid']:APPID;//appid指明這個openid來自哪個應用
$obj['mchid'] = MCHID;//mch_id
$obj['partner_trade_no'] = $arr['partner_trade_no'];//商戶訂單號
$obj['openid'] = $arr['openid'];//接收紅包openid
$obj['amount'] = $arr['amount'];//金額(單位分)
$obj['desc'] = $arr['desc'];
$obj['check_name'] = 'NO_CHECK';//校驗用戶姓名選項,
$obj['spbill_create_ip'] = '******';
$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
$mmpaymktTransfers = new mmpaymktTransfers();
$resarr = $mmpaymktTransfers->postSSLCurl($url, $obj);
return $resarr;
}
//查詢付款情況
function gettransferinfo($m,$p)
{
$arr = $p[0];
$obj['mch_id'] = MCHID;//mch_id
$obj['appid'] = $arr['appid']?$arr['appid']:APPID;//appid指明這個openid來自哪個應用
$obj['partner_trade_no'] = $arr['partner_trade_no'];
$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
$mmpaymktTransfers = new mmpaymktTransfers();
$resarr = $mmpaymktTransfers->postSSLCurl($url, $obj);
return $resarr;
}
$xmlrpc_server = new xmlrpc_server();
$xmlrpc_server->register_method("app_com.sendredpack", "sendredpack");
$xmlrpc_server->register_method("app_com.gethbinfo", "gethbinfo");
$xmlrpc_server->register_method("app_com.transfers", "transfers");
$xmlrpc_server->register_method("app_com.gettransferinfo", "gettransferinfo");
$xmlrpc_server->call_method();
?>
順便帶上xmlrpc.php(馬秉堯版本的)
<?php
/**
* @author 馬秉堯
* @copyright (C) 2005 CoolCode.CN
* @package xmlrpc-epi-php
* @version 0.7
*/
class xmlrpc_error {
var $faultCode;
var $faultString;
function xmlrpc_error($code, $string) {
$this->faultCode = $code;
$this->faultString = $string;
}
}
class xmlrpc_server {
var $server;
function xmlrpc_server() {
$this->server = xmlrpc_server_create();
register_shutdown_function(array(&$this, "__xmlrpc_server"));
}
function register_method($method_name, $function) {
xmlrpc_server_register_method($this->server, $method_name, $function);
}
function xmlrpc_server_add_introspection_data($desc) {
xmlrpc_server_add_introspection_data($this->server, $desc);
}
function register_introspection_callback($function) {
xmlrpc_server_register_introspection_callback($this->server, $function);
}
function call_method($user_data = null) {
if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
$request = $GLOBALS['HTTP_RAW_POST_DATA'];
}
else {
$request = '';
}
$output_options = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8"
);
$response = xmlrpc_server_call_method($this->server, $request, $user_data, $output_options);
header("HTTP/1.1 200 OK");
header("Connection: close");
header("Content-Length: " . strlen($response));
header("Content-Type: text/xml; charset=utf-8");
header("Date: " . gmdate("D, d M Y H:i:s") . " GMT");
print $response;
}
function __xmlrpc_server() {
xmlrpc_server_destroy($this->server);
}
}
class xmlrpc_client {
var $scheme;
var $host;
var $port;
var $path;
var $user;
var $pass;
var $namespace;
var $timeout;
function xmlrpc_client($url, $namespace = '', $user = '', $pass = '', $timeout = 10) {
$this->use_service($url);
$this->namespace = $namespace;
$this->user = $user;
$this->pass = $pass;
$this->timeout = $timeout;
}
function use_service($url) {
$urlparts = parse_url($url);
if (!isset($urlparts['host'])) {
if (isset($_SERVER["HTTP_HOST"])) {
$urlparts['host'] = $_SERVER["HTTP_HOST"];
}
else if (isset($_SERVER["SERVER_NAME"])) {
$urlparts['host'] = $_SERVER["SERVER_NAME"];
}
else {
$urlparts['host'] = "localhost";
}
if (!isset($urlparts['scheme'])) {
if (!isset($_SERVER["HTTPS"]) ||
$_SERVER["HTTPS"] == "off" ||
$_SERVER["HTTPS"] == "") {
$urlparts['scheme'] = "";
}
else {
$urlparts['scheme'] = "https";
}
}
if (!isset($urlparts['port'])) {
$urlparts['port'] = $_SERVER["SERVER_PORT"];
}
}
if (isset($urlparts['scheme']) && ($urlparts['scheme'] == "https")) {
$urlparts['scheme'] = "ssl";
}
else {
$urlparts['scheme'] = "";
}
if (!isset($urlparts['port'])) {
if ($urlparts['scheme'] == "ssl") {
$urlparts['port'] = 443;
}
else {
$urlparts['port'] = 80;
}
}
if (!isset($urlparts['path'])) {
$urlparts['path'] = "/";
}
else if (($urlparts['path']{0} != '/') && ($_SERVER["PHP_SELF"]{0} == '/')) {
$urlparts['path'] = substr($_SERVER["PHP_SELF"], 0, strrpos($_SERVER["PHP_SELF"], '/') + 1) . $urlparts['path'];
}
$this->scheme = $urlparts['scheme'];
$this->host = $urlparts['host'];
$this->port = $urlparts['port'];
$this->path = $urlparts['path'];
}
function __invoke($function, $arguments) {
$output = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8");
$request = xmlrpc_encode_request($function, $arguments, $output);
$content_len = strlen($request);
$errno = 0;
$errstr = '';
$host = ($this->scheme) ? $this->scheme . "://" . $this->host : $this->host;
$handle = @fsockopen($host, $this->port, $errno, $errstr, $this->timeout);
$buf = '';
if ($handle) {
$auth = '';
if ($this->user) {
$auth = "Authorization: Basic " . base64_encode($this->user . ":" . $this->pass) . "\r\n";
}
$http_request =
"POST $this->path HTTP/1.0\r\n" .
"User-Agent: xmlrpc-epi-php/0.6 (PHP)\r\n" .
"Host: $this->host:$this->port\r\n" .
$auth .
"Content-Type: text/xml; charset=utf-8\r\n" .
"Content-Length: $content_len\r\n" .
"\r\n" .
$request;
fputs($handle, $http_request, strlen($http_request));
while (!feof($handle)) {
$buf .= fgets($handle, 128);
}
fclose($handle);
if (strlen($buf)) {
// $xml = substr($buf, strpos($buf, "<?xml"));
$xml = preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/","",substr($buf, strpos($buf, "<?xml")));
if (strlen($xml)) {
$result = xmlrpc_decode($xml);
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(5, "Didn't receive 200 OK from remote server");
}
return $result;
}
function invoke($function, $args) {
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
function call($function, $args) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
}
/*
if (function_exists("overload") && version_compare(phpversion(), "5", "<")) {
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments, &$return) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$return = $this->__invoke($function, $arguments);
return true;
}
}
overload('xmlrpc_client');
}
else {
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
return $this->__invoke($function, $arguments);
}
}
}
*/
?>