提前說一個重要的坑,不注意掉進去,會浪費一些時間。
- 使用者付款到商戶平台的賬戶 與 商戶付款到個人的賬戶是互相獨立的 意思是商戶要付款到個人必須用自己充值到商戶平台賬戶的錢
- 企業付款到個人必須開通此功能 之後才可以使用
- 需要到商戶平台下載下傳支付證書 在使用時不要用官方的demo(2014年的 不怕死的用起來),在引用時直接把檔案存放在伺服器上的絕對路徑寫到函數裡(相對于伺服器的絕對路徑)
- 在算簽名的時候注意 ASCII的順序 大寫字母 < _ < 小寫字母 (惡心的我夠嗆)
- 支付金額必須大于100 機關分
直接上代碼 提現類 并沒有做驗證實作了功能 代碼後面有詳細的流程介紹!!
<?php
/*
微信企業 支付到個人
*/
class cash
{
/**
* array轉xml
*/
public 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;
}
//使用證書,以post方式送出xml到對應的接口url
/**
* 作用:使用證書,以post方式送出xml到對應的接口url
*/
function curl_post_ssl($url, $vars, $second=30)
{
$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);
//以下兩種方式需選擇一種
/******* 此處必須為檔案伺服器根目錄絕對路徑 不可使用變量代替*********/
curl_setopt($ch,CURLOPT_SSLCERT,"/home/lizi/addons/grow/template/mobile/cash/apiclient_cert.pem");
curl_setopt($ch,CURLOPT_SSLKEY,"/home/lizi/addons/grow/template/mobile/cash/apiclient_key.pem");
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;
}
}
//企業向個人付款
public function payToUser($openid=\'oJZJ0w_N_LlTo4AHLnN-cGGtJSeM\',$desc=\'提現成功\',$amount=\'102\')
{
//微信付款到個人的接口
$url = \'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers\';
$params["mch_appid"] = \'\'; //公衆賬号appid
$params["mchid"] = \'\'; //商戶号 微信支付平台賬号
$params["nonce_str"] = \'longdongzhiye99\'.mt_rand(100,999); //随機字元串
$params["partner_trade_no"] = mt_rand(10000000,99999999); //商戶訂單号
$params["amount"] = $amount; //金額
$params["desc"] = $desc; //企業付款描述
$params["openid"] = $openid; //使用者openid
$params["check_name"] = \'NO_CHECK\'; //不檢驗使用者姓名
$params[\'spbill_create_ip\'] = \'123.56.48.18\'; //擷取IP
//生成簽名(簽名算法後面詳細介紹)
$str = \'amount=\'.$params["amount"].\'&check_name=\'.$params["check_name"].\'&desc=\'.$params["desc"].\'&mch_appid=\'.$params["mch_appid"].\'&mchid=\'.$params["mchid"].\'&nonce_str=\'.$params["nonce_str"].\'&openid=\'.$params["openid"].\'&partner_trade_no=\'.$params["partner_trade_no"].\'&spbill_create_ip=\'.$params[\'spbill_create_ip\'].\'&key=F5YguNW77Ao4N5yu5wZ8Lb00NKOg1Y04\';
//md5加密 轉換成大寫
$sign = strtoupper(md5($str));
$params["sign"] = $sign;//簽名
$xml = $this->arrayToXml($params);
return $this->curl_post_ssl($url, $xml);
}
}
/*
$cash = new cash;
$res = $cash -> payToUser();
var_dump($res);
*/
- 要支付必須先配置服務号與微信商戶平台,這裡略。
- 登陸商戶平台開通企業付款到個人
- 如果要實作自動付款
- 首先确定一個方向 就是這是微信的一個接口 帶着參數post請求就好了
- $url = \'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers\'; //請求接口
- 參數(這個玩意可以慢慢說了)
-
參數包含兩個部分 字元串部分 和 證書部分,到這時候你就不要想着跟别的接口似得馬馬虎虎拼拼就出來了,這個過程要慢慢來
字元串部分需要 9個正常參數 + 1個簽名+1個支付密鑰,先說9個參數
字段名 | 變量名 | 必填 | 示例值 | 類型 | 描述 | |
---|---|---|---|---|---|---|
公衆賬号appid | mch_appid | 是 | wx8888888888888888 | String | 公衆号的appId | |
商戶号 | mchid | 是 | 1900000109 | String(32) | 微信支付平台商戶号 就是平台賬号 | |
随機字元串 | nonce_str | 是 | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | String(32) | 随機字元串,随便随機個什麼<32位 | |
商戶訂單号 | partner_trade_no | 是 | 10000098201411111234567890 | String | 訂單号,保持唯一性,自定義一個随機訂單号 | |
使用者 openid | openid | 是 | oxTWIuGaIt6gTKsQRLau2M0yL16E | String | 商戶appid下,某使用者的openid | |
校驗使用者姓名選項 | check_name | 是 | NO_CHECK 不檢驗 (小額推薦) FORCE_CHEC 強制檢驗 OPTION_CHECK 自動檢驗 | String | NO_CHECK:不校驗真實姓名 FORCE_CHECK:強校驗真實姓名(未實名認證的使用者會校驗失敗,無法轉賬) OPTION_CHECK:針對已實名認證的使用者才校驗真實姓名(未實名認證使用者不校驗,可以轉賬成功) | |
收款使用者姓名 | re_user_name | 可選 | 馬花花 (如果上一個參數為強制檢驗此為必填項) | String | 收款使用者真實姓名。 如果check_name設定為FORCE_CHECK或OPTION_CHECK,則必填使用者真實姓名 | |
金額 | amount | 是 | 100 機關為分 100就是100分. | int | 企業付款金額,機關為分 | |
企業付款描述資訊 | desc | 是 | 獎金啊,提現成功啊 退款成功啊什麼的 | String | 企業付款操作說明資訊。必填。 | |
Ip位址 | spbill_create_ip | 是 | 192.168.0.1 | String(32) | 調用接口的機器Ip位址伺服器ip | |
支付密鑰 | key | 是 | F5YguNW77Ao4N5yu5wZ8Lb00NKO987ks | String(32) | 設定在商戶平台上的支付密鑰 | |
簽名 | sign |
| C380BEC2BFD727A4B6845133519F3AD6 | String(32) | 這個惡心了 下面單獨解釋 |
- 簽名是不是一直變動的呢?是的 每一個簽名都是不一樣的,别想着存起來一直用!怎麼算呢?官方有一個文檔,對于會的人來說就是廢話,對于不會的來說就是天書。總的來說分為3部,官方有一個簽名生成工具https://pay.weixin.qq.com/wiki/tools/signverify/。
-
将你本次請求的所有參數(當然除了簽名),按照一定的順序排序成一個字元串,順序一會再說,先說格式,比如本次的這次請求有9個參數:
$str = "amount=100&check_name=NO_CHECK&desc=獎金啊,提現成功啊 退款成功啊什麼的&mch_appid=wx8888888888888888&mchid=1900000109&nonce_str=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&openid=oxTWIuGaIt6gTKsQRLau2M0yL16E&partner_trade_no=10000098201411111234567890&spbill_create_ip=192.168.0.1";
仔細觀察不難發現,字元串排列是有順序的 為鍵值首字母的排列順序。而官方為了聽起來霸氣,講的是根據 參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式排序吓的我一哆嗦啊! 不就字母順序表麼!不過仔細一看發現不對了,比如 mchid和 mch_appid這尼瑪前三個字母一樣啊,一位一位排序下來出現一個 i 和 _怎麼辦呢? 這時候就用到ASCII碼表了,不過看官也不用去查了 上面的可以直接粘去用了 而ASCII碼表的順序呢就是按照0123456789:;< = > ? @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [ \ ] ^ _ ` abcdefghijklmnopqrstuvwxyz { | }~的順序排列 那麼我門就知道mch_appid應該在 mchid 前面了。
-
排序完這9個參數 之後再用&加上特殊參數 微信支付平台上設定的支付密鑰就是
$str = "amount=100&check_name=NO_CHECK&desc=獎金啊,提現成功啊 退款成功啊什麼的&mch_appid=wx8888888888888888&mchid=1900000109&nonce_str=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&openid=oxTWIuGaIt6gTKsQRLau2M0yL16E&partner_trade_no=10000098201411111234567890&spbill_create_ip=192.168.0.1&key=F5YguNW77Ao4N5yu5wZ8Lb00NKO987ks"
-
之後就簡單了先md5加密下然後轉為大寫 簽名就OK了
$sign = strtoupper(md5($str));
然後我們就要将這些參數填充到xml格式的字元串中去了
官方示例為
<xml>
<mch_appid>wxe062425f740c30d8</mch_appid>
<mchid>10000098</mchid>
<nonce_str>3PG2J4ILTKCH16CQ2502SI8ZNMTM67VS</nonce_str>
<partner_trade_no>100000982014120919616</partner_trade_no>
<openid>ohO4Gt7wVPxIT1A9GjFaMYMiZY1s</openid>
<check_name>OPTION_CHECK</check_name>
<re_user_name>張三</re_user_name>
<amount>100</amount>
<desc>節日快樂!</desc>
<spbill_create_ip>10.2.3.10</spbill_create_ip>
<sign>C97BDBACF37622775366F38B629F45E3</sign>
</xml>
我個人還是覺的将參數都存到數組裡,然後周遊拼接出來比較好例如:
$params["mch_appid"] = \'\'; //公衆賬号appid
$params["mchid"] = \'\'; //商戶号 微信支付平台賬号
$params["nonce_str"] = \'longdongzhiye99\'.mt_rand(100,999); //随機字元串
$params["partner_trade_no"] = mt_rand(10000000,99999999); //商戶訂單号
$params["amount"] = $amount; //金額
$params["desc"] = $desc; //企業付款描述
$params["openid"] = $openid; //使用者openid
$params["check_name"] = \'NO_CHECK\'; //不檢驗使用者姓名
$params[\'spbill_create_ip\'] = \'123.56.48.18\'; //擷取IP
//生成簽名(簽名算法後面詳細介紹)
$str = \'amount=\'.$params["amount"].\'&check_name=\'.$params["check_name"].\'&desc=\'.$params["desc"].\'&mch_appid=\'.$params["mch_appid"].\'&mchid=\'.$params["mchid"].\'&nonce_str=\'.$params["nonce_str"].\'&openid=\'.$params["openid"].\'&partner_trade_no=\'.$params["partner_trade_no"].\'&spbill_create_ip=\'.$params[\'spbill_create_ip\'].\'&key=F5YguNW77Ao4N5yu5wZ8Lb00NKOg1Y04\';
//md5加密 轉換成大寫
$sign = strtoupper(md5($str));
$params["sign"] = $sign;//簽名
然後拼接
$xml = "<xml>";
foreach ($paramsas $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else{
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
$xml .= "</xml>";
得到$xml。
到此字元傳參數部分完成了,然後還沒完呢!還有證書部分
之後用curl函數檢測證書 之後頁要用curl發送請求 直接貼代碼解釋
/**
* 作用:使用證書,以post方式送出xml到對應的接口url
*/
function curl_post_ssl($url, $xml, $second=30)
{
$ch = curl_init();
//逾時時間
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//将curl_exec()擷取的資訊以檔案流的形式傳回,而不是直接輸出。
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_URL,$url);
//根據curl版本有不同預設值 設定一下放心
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
//以下兩種方式需選擇一種 雙向檢測證書
/******* 此處必須為檔案伺服器根目錄絕對路徑 不可使用變量代替*********/
curl_setopt($ch,CURLOPT_SSLCERT,"/home/lizi/addons/grow/template/mobile/cash/apiclient_cert.pem");
curl_setopt($ch,CURLOPT_SSLKEY,"/home/lizi/addons/grow/template/mobile/cash/apiclient_key.pem");
curl_setopt($ch,CURLOPT_POST, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$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;
}
}
貼出成功之後傳回碼
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[]]></return_msg>
<mch_appid><![CDATA[wxec38b8ff840bd989]]></mch_appid>
<mchid><![CDATA[10013274]]></mchid>
<device_info><![CDATA[]]></device_info>
<nonce_str><![CDATA[lxuDzMnRjpcXzxLx0q]]></nonce_str>
<result_code><![CDATA[SUCCESS]]></result_code>
<partner_trade_no><![CDATA[10013574201505191526582441]]></partner_trade_no>
<payment_no><![CDATA[1000018301201505190181489473]]></payment_no>
<payment_time><![CDATA[2015-05-19 15:26:59]]></payment_time>
</xml>
失敗的傳回碼會提示錯誤資訊 就不做介紹了。