PHP微信支付開(kāi)發(fā)實(shí)例
PHP微信支付開(kāi)發(fā)過(guò)程,分享給大家,供大家參考,具體內(nèi)容如下
1.開(kāi)發(fā)環(huán)境
Thinkphp 3.2.3
微信:服務(wù)號(hào),已認(rèn)證
開(kāi)發(fā)域名:http://test.paywechat.com (自定義的域名,外網(wǎng)不可訪(fǎng)問(wèn))
2.需要相關(guān)文件和權(quán)限
微信支付需申請(qǐng)開(kāi)通
微信公眾平臺(tái)開(kāi)發(fā)者文檔:http://mp.weixin.qq.com/wiki/home/index.html
微信支付開(kāi)發(fā)者文檔:https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付SDK下載地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
3.開(kāi)發(fā)
下載好微信支付PHP版本的SDK,文件目錄為下圖:

把微信支付SDK的Cert和Lib目錄放入Thinkphp,目錄為
現(xiàn)在介紹微信支付授權(quán)目錄問(wèn)題,首先是微信支付開(kāi)發(fā)配置里面的支付授權(quán)目錄填寫(xiě),

然后填寫(xiě)JS接口安全域。

最后設(shè)置網(wǎng)頁(yè)授權(quán)


這些設(shè)置完,基本完成一半,注意設(shè)置的目錄和我thinkphp里面的目錄。

4.微信支付配置

把相關(guān)配置填寫(xiě)正確。
/**
* 配置賬號(hào)信息
*/
class WxPayConfig
{
//=======【基本信息設(shè)置】=====================================
//
/**
* TODO: 修改這里配置為您自己申請(qǐng)的商戶(hù)信息
* 微信公眾號(hào)信息配置
*
* APPID:綁定支付的APPID(必須配置,開(kāi)戶(hù)郵件中可查看)
*
* MCHID:商戶(hù)號(hào)(必須配置,開(kāi)戶(hù)郵件中可查看)
*
* KEY:商戶(hù)支付密鑰,參考開(kāi)戶(hù)郵件設(shè)置(必須配置,登錄商戶(hù)平臺(tái)自行設(shè)置)
* 設(shè)置地址:https://pay.weixin.qq.com/index.php/account/api_cert
*
* APPSECRET:公眾帳號(hào)secert(僅JSAPI支付的時(shí)候需要配置, 登錄公眾平臺(tái),進(jìn)入開(kāi)發(fā)者中心可設(shè)置),
* 獲取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
* @var string
*/
const APPID = '';
const MCHID = '';
const KEY = '';
const APPSECRET = '';
//=======【證書(shū)路徑設(shè)置】=====================================
/**
* TODO:設(shè)置商戶(hù)證書(shū)路徑
* 證書(shū)路徑,注意應(yīng)該填寫(xiě)絕對(duì)路徑(僅退款、撤銷(xiāo)訂單時(shí)需要,可登錄商戶(hù)平臺(tái)下載,
* API證書(shū)下載地址:https://pay.weixin.qq.com/index.php/account/api_cert,下載之前需要安裝商戶(hù)操作證書(shū))
* @var path
*/
const SSLCERT_PATH = '../cert/apiclient_cert.pem';
const SSLKEY_PATH = '../cert/apiclient_key.pem';
//=======【curl代理設(shè)置】===================================
/**
* TODO:這里設(shè)置代理機(jī)器,只有需要代理的時(shí)候才設(shè)置,不需要代理,請(qǐng)?jiān)O(shè)置為0.0.0.0和0
* 本例程通過(guò)curl使用HTTP POST方法,此處可修改代理服務(wù)器,
* 默認(rèn)CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此時(shí)不開(kāi)啟代理(如有需要才設(shè)置)
* @var unknown_type
*/
const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
const CURL_PROXY_PORT = 0;//8080;
//=======【上報(bào)信息配置】===================================
/**
* TODO:接口調(diào)用上報(bào)等級(jí),默認(rèn)緊錯(cuò)誤上報(bào)(注意:上報(bào)超時(shí)間為【1s】,上報(bào)無(wú)論成敗【永不拋出異?!?,
* 不會(huì)影響接口調(diào)用流程),開(kāi)啟上報(bào)之后,方便微信監(jiān)控請(qǐng)求調(diào)用的質(zhì)量,建議至少
* 開(kāi)啟錯(cuò)誤上報(bào)。
* 上報(bào)等級(jí),0.關(guān)閉上報(bào); 1.僅錯(cuò)誤出錯(cuò)上報(bào); 2.全量上報(bào)
* @var int
*/
const REPORT_LEVENL = 1;
}
現(xiàn)在開(kāi)始貼出代碼:
namespace Wechat\Controller;
use Think\Controller;
/**
* 父類(lèi)控制器,需要繼承
* @file ParentController.class.php
* @author Gary <lizhiyong2204@sina.com>
* @date 2015年8月4日
* @todu
*/
class ParentController extends Controller {
protected $options = array (
'token' => '', // 填寫(xiě)你設(shè)定的key
'encodingaeskey' => '', // 填寫(xiě)加密用的EncodingAESKey
'appid' => '', // 填寫(xiě)高級(jí)調(diào)用功能的app id
'appsecret' => '', // 填寫(xiě)高級(jí)調(diào)用功能的密鑰
'debug' => false,
'logcallback' => ''
);
public $errCode = 40001;
public $errMsg = "no access";
/**
* 獲取access_token
* @return mixed|boolean|unknown
*/
public function getToken(){
$cache_token = S('exp_wechat_pay_token');
if(!empty($cache_token)){
return $cache_token;
}
$url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';
$url = sprintf($url,$this->options['appid'],$this->options['appsecret']);
$result = $this->http_get($url);
$result = json_decode($result,true);
if(empty($result)){
return false;
}
S('exp_wechat_pay_token',$result['access_token'],array('type'=>'file','expire'=>3600));
return $result['access_token'];
}
/**
* 發(fā)送客服消息
* @param array $data 消息結(jié)構(gòu){"touser":"OPENID","msgtype":"news","news":{...}}
*/
public function sendCustomMessage($data){
$token = $this->getToken();
if (empty($token)) return false;
$url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s';
$url = sprintf($url,$token);
$result = $this->http_post($url,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 發(fā)送模板消息
* @param unknown $data
* @return boolean|unknown
*/
public function sendTemplateMessage($data){
$token = $this->getToken();
if (empty($token)) return false;
$url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
$url = sprintf($url,$token);
$result = $this->http_post($url,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
public function getFileCache($name){
return S($name);
}
/**
* 微信api不支持中文轉(zhuǎn)義的json結(jié)構(gòu)
* @param array $arr
*/
static function json_encode($arr) {
$parts = array ();
$is_list = false;
//Find out if the given array is a numerical array
$keys = array_keys ( $arr );
$max_length = count ( $arr ) - 1;
if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
$is_list = true;
for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position
if ($i != $keys [$i]) { //A key fails at position check.
$is_list = false; //It is an associative array.
break;
}
}
}
foreach ( $arr as $key => $value ) {
if (is_array ( $value )) { //Custom handling for arrays
if ($is_list)
$parts [] = self::json_encode ( $value ); /* :RECURSION: */
else
$parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */
} else {
$str = '';
if (! $is_list)
$str = '"' . $key . '":';
//Custom handling for multiple data types
if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)
$str .= $value; //Numbers
elseif ($value === false)
$str .= 'false'; //The booleans
elseif ($value === true)
$str .= 'true';
else
$str .= '"' . addslashes ( $value ) . '"'; //All other things
// :TODO: Is there any more datatype we should be in the lookout for? (Object?)
$parts [] = $str;
}
}
$json = implode ( ',', $parts );
if ($is_list)
return '[' . $json . ']'; //Return numerical JSON
return '{' . $json . '}'; //Return associative JSON
}
/**
+----------------------------------------------------------
* 生成隨機(jī)字符串
+----------------------------------------------------------
* @param int $length 要生成的隨機(jī)字符串長(zhǎng)度
* @param string $type 隨機(jī)碼類(lèi)型:0,數(shù)字+大小寫(xiě)字母;1,數(shù)字;2,小寫(xiě)字母;3,大寫(xiě)字母;4,特殊字符;-1,數(shù)字+大小寫(xiě)字母+特殊字符
+----------------------------------------------------------
* @return string
+----------------------------------------------------------
*/
static public function randCode($length = 5, $type = 2){
$arr = array(1 => "0123456789", 2 => "abcdefghijklmnopqrstuvwxyz", 3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4 => "~@#$%^&*(){}[]|");
if ($type == 0) {
array_pop($arr);
$string = implode("", $arr);
} elseif ($type == "-1") {
$string = implode("", $arr);
} else {
$string = $arr[$type];
}
$count = strlen($string) - 1;
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $string[rand(0, $count)];
}
return $code;
}
/**
* GET 請(qǐng)求
* @param string $url
*/
private function http_get($url){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
/**
* POST 請(qǐng)求
* @param string $url
* @param array $param
* @param boolean $post_file 是否文件上傳
* @return string content
*/
private function http_post($url,$param,$post_file=false){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
if (is_string($param) || $post_file) {
$strPOST = $param;
} else {
$aPOST = array();
foreach($param as $key=>$val){
$aPOST[] = $key."=".urlencode($val);
}
$strPOST = join("&", $aPOST);
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($oCurl, CURLOPT_POST,true);
curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
}
namespace Wechat\Controller;
use Wechat\Controller\ParentController;
/**
* 微信支付測(cè)試控制器
* @file TestController.class.php
* @author Gary <lizhiyong2204@sina.com>
* @date 2015年8月4日
* @todu
*/
class TestController extends ParentController {
private $_order_body = 'xxx';
private $_order_goods_tag = 'xxx';
public function __construct(){
parent::__construct();
require_once ROOT_PATH."Api/lib/WxPay.Api.php";
require_once ROOT_PATH."Api/lib/WxPay.JsApiPay.php";
}
public function index(){
//①、獲取用戶(hù)openid
$tools = new \JsApiPay();
$openId = $tools->GetOpenid();
//②、統(tǒng)一下單
$input = new \WxPayUnifiedOrder();
//商品描述
$input->SetBody($this->_order_body);
//附加數(shù)據(jù),可以添加自己需要的數(shù)據(jù),微信回異步回調(diào)時(shí)會(huì)附加這個(gè)數(shù)據(jù)
$input->SetAttach('xxx');
//商戶(hù)訂單號(hào)
$out_trade_no = \WxPayConfig::MCHID.date("YmdHis");
$input->SetOut_trade_no($out_trade_no);
//總金額,訂單總金額,只能為整數(shù),單位為分
$input->SetTotal_fee(1);
//交易起始時(shí)間
$input->SetTime_start(date("YmdHis"));
//交易結(jié)束時(shí)間
$input->SetTime_expire(date("YmdHis", time() + 600));
//商品標(biāo)記
$input->SetGoods_tag($this->_order_goods_tag);
//通知地址,接收微信支付異步通知回調(diào)地址 SITE_URL=http://test.paywechat.com/Charge
$notify_url = SITE_URL.'/index.php/Test/notify.html';
$input->SetNotify_url($notify_url);
//交易類(lèi)型
$input->SetTrade_type("JSAPI");
$input->SetOpenid($openId);
$order = \WxPayApi::unifiedOrder($input);
$jsApiParameters = $tools->GetJsApiParameters($order);
//獲取共享收貨地址js函數(shù)參數(shù)
$editAddress = $tools->GetEditAddressParameters();
$this->assign('openId',$openId);
$this->assign('jsApiParameters',$jsApiParameters);
$this->assign('editAddress',$editAddress);
$this->display();
}
/**
* 異步通知回調(diào)方法
*/
public function notify(){
require_once ROOT_PATH."Api/lib/notify.php";
$notify = new \PayNotifyCallBack();
$notify->Handle(false);
//這里的IsSuccess是我自定義的一個(gè)方法,后面我會(huì)貼出這個(gè)文件的代碼,供參考。
$is_success = $notify->IsSuccess();
$bdata = $is_success['data'];
//支付成功
if($is_success['code'] == 1){
$news = array(
'touser' => $bdata['openid'],
'msgtype' => 'news',
'news' => array (
'articles'=> array (
array(
'title' => '訂單支付成功',
'description' => "支付金額:{$bdata['total_fee']}\n".
"微信訂單號(hào):{$bdata['transaction_id']}\n"
'picurl' => '',
'url' => ''
)
)
)
);
//發(fā)送微信支付通知
$this->sendCustomMessage($news);
}else{//支付失敗
}
}
/**
* 支付成功頁(yè)面
* 不可靠的回調(diào)
*/
public function ajax_PaySuccess(){
//訂單號(hào)
$out_trade_no = I('post.out_trade_no');
//支付金額
$total_fee = I('post.total_fee');
/*相關(guān)邏輯處理*/
}
貼上模板HTML
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>微信支付樣例-支付</title>
<script type="text/javascript">
//調(diào)用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{$jsApiParameters},
function(res){
WeixinJSBridge.log(res.err_msg);
//取消支付
if(res.err_msg == 'get_brand_wcpay_request:cancel'){
//處理取消支付的事件邏輯
}else if(res.err_msg == "get_brand_wcpay_request:ok"){
/*使用以上方式判斷前端返回,微信團(tuán)隊(duì)鄭重提示:
res.err_msg將在用戶(hù)支付成功后返回 ok,但并不保證它絕對(duì)可靠。
這里可以使用Ajax提交到后臺(tái),處理一些日志,如Test控制器里面的ajax_PaySuccess方法。
*/
}
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();
}
}
//獲取共享地址
function editAddress()
{
WeixinJSBridge.invoke(
'editAddress',
{$editAddress},
function(res){
var value1 = res.proviceFirstStageName;
var value2 = res.addressCitySecondStageName;
var value3 = res.addressCountiesThirdStageName;
var value4 = res.addressDetailInfo;
var tel = res.telNumber;
alert(value1 + value2 + value3 + value4 + ":" + tel);
}
);
}
window.onload = function(){
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', editAddress, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', editAddress);
document.attachEvent('onWeixinJSBridgeReady', editAddress);
}
}else{
editAddress();
}
};
</script>
</head>
<body>
<br/>
<font color="#9ACD32"><b>該筆訂單支付金額為<span style="color:#f00;font-size:50px">1分</span>錢(qián)</b></font><br/><br/>
<div align="center">
<button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
</div>
</body>
</html>
notify.php文件代碼,這里有在官方文件里新添加的一個(gè)自定義方法。
require_once ROOT_PATH."Api/lib/WxPay.Api.php";
require_once ROOT_PATH.'Api/lib/WxPay.Notify.php';
require_once ROOT_PATH.'Api/lib/log.php';
//初始化日志
$logHandler= new \CLogFileHandler(ROOT_PATH."/logs/".date('Y-m-d').'.log');
$log = \Log::Init($logHandler, 15);
class PayNotifyCallBack extends WxPayNotify
{
protected $para = array('code'=>0,'data'=>'');
//查詢(xún)訂單
public function Queryorder($transaction_id)
{
$input = new \WxPayOrderQuery();
$input->SetTransaction_id($transaction_id);
$result = \WxPayApi::orderQuery($input);
\Log::DEBUG("query:" . json_encode($result));
if(array_key_exists("return_code", $result)
&& array_key_exists("result_code", $result)
&& $result["return_code"] == "SUCCESS"
&& $result["result_code"] == "SUCCESS")
{
return true;
}
$this->para['code'] = 0;
$this->para['data'] = '';
return false;
}
//重寫(xiě)回調(diào)處理函數(shù)
public function NotifyProcess($data, &$msg)
{
\Log::DEBUG("call back:" . json_encode($data));
$notfiyOutput = array();
if(!array_key_exists("transaction_id", $data)){
$msg = "輸入?yún)?shù)不正確";
$this->para['code'] = 0;
$this->para['data'] = '';
return false;
}
//查詢(xún)訂單,判斷訂單真實(shí)性
if(!$this->Queryorder($data["transaction_id"])){
$msg = "訂單查詢(xún)失敗";
$this->para['code'] = 0;
$this->para['data'] = '';
return false;
}
$this->para['code'] = 1;
$this->para['data'] = $data;
return true;
}
/**
* 自定義方法 檢測(cè)微信端是否回調(diào)成功方法
* @return multitype:number string
*/
public function IsSuccess(){
return $this->para;
}
}
到這里基本上完成,可以在微信端打開(kāi)http://test.paywechat.com/Charge/index.php/Test/index/
我的環(huán)境,HTTP服務(wù)器沒(méi)有重寫(xiě)url,微信支付繼續(xù)探索中,有些地方可能寫(xiě)的有問(wèn)題或不足,望大家諒解,互相學(xué)習(xí)。
以上就是PHP微信支付開(kāi)發(fā)的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 微信支付PHP SDK之微信公眾號(hào)支付代碼詳解
- PHP實(shí)現(xiàn)微信支付(jsapi支付)流程步驟詳解
- ThinkPHP實(shí)現(xiàn)微信支付(jsapi支付)流程教程詳解
- php微信支付之APP支付方法
- 微信小程序 支付功能實(shí)現(xiàn)PHP實(shí)例詳解
- PHP實(shí)現(xiàn)QQ、微信和支付寶三合一收款碼實(shí)例代碼
- 微信小程序與php 實(shí)現(xiàn)微信支付的簡(jiǎn)單實(shí)例
- ThinkPHP框架實(shí)現(xiàn)的微信支付接口開(kāi)發(fā)完整示例
- ThinkPHP框架整合微信支付之JSAPI模式圖文詳解
- ThinkPHP框架整合微信支付之Native 掃碼支付模式一圖文詳解
- ThinkPHP框架整合微信支付之Native 掃碼支付模式二圖文詳解
- PHP實(shí)現(xiàn)的微信APP支付功能示例【基于TP5框架】
相關(guān)文章
完美的2個(gè)php檢測(cè)字符串是否是utf-8編碼函數(shù)分享
這篇文章主要介紹了完美的2個(gè)php檢測(cè)字符串是否是utf-8編碼函數(shù)分享,一個(gè)比較強(qiáng)大、一個(gè)比較簡(jiǎn)潔,需要的朋友可以參考下2014-07-07
PHP 在數(shù)組中搜索給定的簡(jiǎn)單實(shí)例 array_search 函數(shù)
下面小編就為大家?guī)?lái)一篇PHP 在數(shù)組中搜索給定的簡(jiǎn)單實(shí)例 array_search 函數(shù)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06
安裝PHP擴(kuò)展時(shí)解壓官方 tgz 文件后沒(méi)有configure文件無(wú)法進(jìn)行配置編譯的問(wèn)題
這篇文章主要介紹了安裝PHP擴(kuò)展時(shí)解壓官方 tgz 文件后沒(méi)有configure文件無(wú)法進(jìn)行配置編譯的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-08-08
Laravel認(rèn)證原理以及完全自定義認(rèn)證詳解
最近在學(xué)習(xí)laravel框架,所以下面這篇文章主要給大家介紹了關(guān)于Laravel認(rèn)證原理以及完全自定義認(rèn)證的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
PHP實(shí)現(xiàn)linux命令tail -f
tail 命令從指定點(diǎn)開(kāi)始將文件寫(xiě)到標(biāo)準(zhǔn)輸出.使用tail命令的-f選項(xiàng)可以方便的查閱正在改變的日志文件,tail -f filename會(huì)把filename里最尾部的內(nèi)容顯示在屏幕上,并且不但刷新,使你看到最新的文件內(nèi)容.接下來(lái)通過(guò)本文給大家介紹PHP實(shí)現(xiàn)linux命令tail -f,需要的朋友參考下2016-02-02

