搜尋
首頁後端開發php教程PHP:微信支付服務商開發相關的那點事

PHP:微信支付服務商開發相關的那點事

項目背景

不是什麼大項目,使用微信服務商來管理多個子商戶,並使用服務商的介面替子商戶下單,服務商後台才能接收到回呼

使用場景是web掃碼支付

#準備

網域應該要在服務商所歸屬的公眾號碼內「網頁授權網域」設定好(不知此操作是否需要?)

在付款服務商後台設定好回呼位址(子商家應該是不用設定)

專案使用apache php為後台服務,下載官方支付php demo(native)

我們直接先按照demo的目錄結構來玩,直接把解壓縮的example和lib,2個目錄都到伺服器根目錄

在example目錄下,建立cert目錄,進服務商後台-帳戶中心-api安全,下載證書,放到這個目錄內

在example目錄下,建立logs目錄,用於微信支付log類寫日誌檔

由於微信支付相關都要使用https,所以查看訪問日誌在apache目錄下的logs目錄,ssl_request.txt文件,最下面,可以看到回呼地址是否被請求

注意

官方demo有2個方式的掃碼支付,第一種方式已經不提供,都使用第二種

官方的demo ,會有不能顯示二維碼的bug,範例頁是native.php

列印print_r($result);  這個,會顯示錯誤,主要是關於於curl的錯誤,自行百度解決

配置

在WxPay.Config.Interface.php中的介面物件中增加一個公共方法public abstract function GetSubMchId(); //取得子商家id在WxPay.Config .php內中,配置所需的參數,自行百度,並且增加一個方法public function GetSubMchId(){   return '8888888888'; //返回子商家號碼by vbyzc   }在lib/WxPay.Api.php 內,在統一下單方法unifiedOrder中,下面的參數那段位置,增加$inputObj->SetSub_mch_id($config->GetSubMchId());//子商家號碼by vbyzc在各個需要查詢訂單的地方回調,付款頁面即時偵測訂單支付狀態的請求頁面,都要使用此方法來設定子商家id:
$input->SetSub_mch_id($config->GetSubMchId());注意,有的地方可能沒有$config對象,請引入WxPay.Config.php ,並初始化:$config = new WxPayConfig();

部分程式碼

掃碼頁面:native.php

<?php
/**
*
* example目录下为简单的支付样例,仅能用于搭建快速体验微信支付使用
* 样例的作用仅限于指导如何使用sdk,在安全上面仅做了简单处理, 复制使用样例代码时请慎重
* 请勿直接直接使用样例对外提供服务
* 
**/

require_once "../lib/WxPay.Api.php";
require_once "WxPay.NativePay.php";
require_once &#39;log.php&#39;;

//初始化日志
$logHandler= new CLogFileHandler("logs/".date(&#39;Y-m-d&#39;).&#39;.log&#39;);
$log = Log::Init($logHandler, 15);

//模式一
//官方不再提供模式一支付方式

$notify = new NativePay();

//模式二
/**
 * 流程:
 * 1、调用统一下单,取得code_url,生成二维码
 * 2、用户扫描二维码,进行支付
 * 3、支付完成之后,微信服务器会通知支付成功
 * 4、在支付成功通知中需要查单确认是否真正支付成功(见:notify.php)
 */

$out_trade_no = "vbyzc_for_jstx".date("YmdHis"); 

$input = new WxPayUnifiedOrder();
$input->SetBody("test_body");
$input->SetAttach("test_Attach");//成功支付的回调里会返回这个
$input->SetOut_trade_no($out_trade_no);//自定义订单号
$input->SetTotal_fee("1"); // 金额
$input->SetTime_start(date("YmdHis"));
// $input->SetTime_expire(date("YmdHis", time() + 500));
$input->SetGoods_tag("test_goodsTag");
$input->SetNotify_url("https://service.ktfqs.com/example/wx_pay_callback.php");
$input->SetTrade_type("NATIVE");
$input->SetProduct_id("123456789"); //此id为二维码中包含的商品ID,商户自行定义。

$result = $notify->GetPayUrl($input);
$url2 = $result["code_url"];

echo "<div>这是返回:$url2</div>";
print_r($result);
?>

<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 src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
</head>
<body>

    <div style="margin-left: 10px;color:#556B2F;font-size:30px;font-weight: bolder;">扫描支付模式二</div><br/>
    <div> 订单编号<input id="out_trade_no" type="hidden"  value="<?php echo $out_trade_no;?>"> </div>
    <img  src="/static/imghwm/default1.png"  data-src="qrcode.php?data=<?php echo urlencode($url2);? alt="PHP:微信支付服務商開發相關的那點事" >"  class="lazy"  alt="模式二扫码支付"    style="max-width:90%"/>
    <div>支付提示:<span id="query_result" style="color: red">WAITING...</span></div>
    <script>
        var t1;
        var sum=0;
        $(document).ready(function () {
            t1=setInterval("ajaxstatus()", 4000);
        });
        function ajaxstatus() {
            sum++;
            if(sum>100){ window.clearInterval(t1);return false;}
            if ($("#out_trade_no").val() != 0) {
                $.post("orderqueryajax.php", { out_trade_no:$("#out_trade_no").val() }, function (data) {
                    data = $.trim(data);
                    $("#query_result").html(data);
                    if (data=="SUCCESS") {
                        $("#query_result").html("哈哈哈!!支付成功,即将跳转...");
                        window.clearInterval(t1)
                        <?php
                            // 插入php代码
                            /*
                            if (isset($_POST[&#39;history_go&#39;]) && $_POST[&#39;history_go&#39;] == 3){
                                echo &#39;window.setTimeout("history.go(-3);",2000);&#39;;
                            }else{
                                echo &#39;window.setTimeout("history.go(-2);",2000);&#39;;
                            }
                            */
                        ?>
                    }
                });
            }
        }
    </script>
</body>
</html>

查詢並回傳訂單狀態頁面:orderqueryajax.php

<?php
/**
*
* ajax异步查询订单是否完成
* 
**/
require_once "../lib/WxPay.Api.php";
require_once &#39;log.php&#39;;
require_once "WxPay.Config.php";

//初始化日志
$logHandler= new CLogFileHandler("../logs/".date(&#39;Y-m-d&#39;).&#39;.log&#39;);
$log = Log::Init($logHandler, 15);

$v = $_POST["out_trade_no"];
if(isset($v) && $v != ""){
    $out_trade_no = $v;
    $config = new WxPayConfig();
    $input = new WxPayOrderQuery();
    $input->SetOut_trade_no($out_trade_no);
    $input->SetSub_mch_id($config->GetSubMchId());//子商户号 by vbyzc
    $result = WxPayApi::orderQuery($config, $input);
    if ($result[&#39;return_code&#39;] == &#39;SUCCESS&#39; && $result[&#39;result_code&#39;] == &#39;SUCCESS&#39;){//返回查询结果
        echo $result[&#39;trade_state&#39;];
    }else{
        echo "FAIL";
    }
}
?>

回呼頁:notify.php

<?php
date_default_timezone_set(&#39;PRC&#39;);
/**
*
* example目录下为简单的支付样例,仅能用于搭建快速体验微信支付使用
* 样例的作用仅限于指导如何使用sdk,在安全上面仅做了简单处理, 复制使用样例代码时请慎重
* 请勿直接直接使用样例对外提供服务
* 
**/
// 链接数据库
include_once(&#39;../include/conn_db.php&#39;);
include_once(&#39;../include/db_class.php&#39;);
mysql_connect(HOST,NAME,PASS) or die(mysql_error());
mysql_select_db(DBNAME);
mysql_query(&#39;SET NAMES &#39;.CODEPAGE);

require_once "../lib/WxPay.Api.php";
require_once &#39;../lib/WxPay.Notify.php&#39;;
require_once "WxPay.Config.php";
require_once &#39;log.php&#39;;

//初始化日志
$logHandler= new CLogFileHandler("logs/".date(&#39;Y-m-d&#39;).&#39;.log&#39;);
$log = Log::Init($logHandler, 15);

class PayNotifyCallBack extends WxPayNotify
{
    //查询订单
    public function Queryorder($transaction_id)
    {
        $input = new WxPayOrderQuery();
        $config = new WxPayConfig();
        $input->SetTransaction_id($transaction_id);
        $input->SetSub_mch_id($config->GetSubMchId()); //设置子商户号  by vbyzc
        $result = WxPayApi::orderQuery($config, $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;
        }
        return false;
    }

    /**
    *
    * 回包前的回调方法
    * 业务可以继承该方法,打印日志方便定位
    * @param string $xmlData 返回的xml参数
    *
    **/
    public function LogAfterProcess($xmlData)
    {
        Log::DEBUG("call back, return xml:" . $xmlData);
        return;
    }
    
    //重写回调处理函数
    /**
     * @param WxPayNotifyResults $data 回调解释出的参数
     * @param WxPayConfigInterface $config
     * @param string $msg 如果回调处理失败,可以将错误信息输出到该方法
     * @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
     */
    public function NotifyProcess($objData, $config, &$msg)
    {
        $data = $objData->GetValues();
        //TODO 1、进行参数校验
        if(!array_key_exists("return_code", $data) 
            ||(array_key_exists("return_code", $data) && $data[&#39;return_code&#39;] != "SUCCESS")) {
            //TODO失败,不是支付成功的通知
            //如果有需要可以做失败时候的一些清理处理,并且做一些监控
            $msg = "异常异常";
            return false;
        }
        if(!array_key_exists("transaction_id", $data)){
            $msg = "输入参数不正确";
            return false;
        }

        //TODO 2、进行签名验证
        try {
            $checkResult = $objData->CheckSign($config);
            if($checkResult == false){
                //签名错误
                Log::ERROR("签名错误...");
                return false;
            }
        } catch(Exception $e) {
            Log::ERROR(json_encode($e));
        }

        //TODO 3、处理业务逻辑
        Log::DEBUG("call back JSON:" . json_encode($data));
        $notfiyOutput = array();
        /* 返回的格式 
        {
            "appid": "wxa664cef2fee1b641", //调用接口提交的公众账号ID
            "attach": "test",//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 (使用SetAttach设置的)
            "bank_type": "LQT",//不知什么鬼东西
            "cash_fee": "1",// 金额
            "fee_type": "CNY",//货币类型
            "is_subscribe": "N",//不知什么鬼东西
            "mch_id": "154133502151",// 商户号(服务商)
            "nonce_str": "jw0bvddz275qyvxnpdfoaam55h3dw6uk",//微信返回的随机字符串
            "openid": "opnVE5pDPx2hWAoLLxyQW5KQt8GA",// 用户openid(应该是对于绑定的公从号)
            "out_trade_no": "vbyzc_for_jstx20190701010509",// 发起订单时自定义订单号
            "result_code": "SUCCESS",// 业务结果
            "return_code": "SUCCESS",// 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
            "sign": "80E46C6CC50C25E6B5099AE4E03DA3C6FEFD5B172A99B03A56FAC4A9E11EC8F3",//
            "sub_mch_id": "154172463171",// 子商户id
            "time_end": "20190701090530",// 交易结束时间??
            "total_fee": "1",// 总金额
            "trade_type": "NATIVE",// 支付方式
            "transaction_id": "4200000301201907011310094985" // 微信支付单号
        }
        */
        //查询订单,判断订单真实性
        if(!$this->Queryorder($data["transaction_id"])){
            $msg = "订单查询失败";
            Log::DEBUG("vbyzc run to here : order querySelect faild!!!!!" );
            return false;
        }
        // 根据微信官方原代码的业务流程,应该是如下:
        // 支会成功后微信会不断请求回调,在上面的代码 应该是包函了回调回应的代码,
        // 如果成功回应,微信支付应该就停止请求回调,才能执行下面的代码 
        Log::DEBUG("vbyzc run to here :<<<<<<<<<<<<<<start to mysql record" );

        $openid = $data[&#39;openid&#39;];// 微信用户
        $trade_no = $data[&#39;transaction_id&#39;];// 微信支付单号
        $mch_id = $data[&#39;mch_id&#39;];// 商户号
        $sub_mch_id = $data[&#39;sub_mch_id&#39;];// 子商户id
        $trade_status = $data[&#39;result_code&#39;];// 业务结果
        $total_amount = $data[&#39;total_fee&#39;];// 总金额
        $out_trade_no = $data[&#39;out_trade_no&#39;];// 商户自定义订单号

        $cmd = "insert into myorder(openid,trade_no,mch_id,sub_mch_id,trade_status,total_amount,out_trade_no,datetime) 
        values (&#39;$openid&#39;,&#39;$trade_no&#39;,&#39;$mch_id&#39;,&#39;$sub_mch_id&#39;,&#39;$trade_status&#39;,$total_amount,&#39;$out_trade_no&#39;,NOW())";
        mysql_query($cmd);
        Log::DEBUG("vbyzc run to here :end to mysql record>>>>>>>>>>" );
        return true;
    }
}

$config = new WxPayConfig();
Log::DEBUG("begin notify");
$notify = new PayNotifyCallBack();
$notify->Handle($config, false);


?>

更多PHP相關技術文章,請造訪PHP教學欄位進行學習!

以上是PHP:微信支付服務商開發相關的那點事的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
您如何防止與會議有關的跨站點腳本(XSS)攻擊?您如何防止與會議有關的跨站點腳本(XSS)攻擊?Apr 23, 2025 am 12:16 AM

要保護應用免受與會話相關的XSS攻擊,需採取以下措施:1.設置HttpOnly和Secure標誌保護會話cookie。 2.對所有用戶輸入進行輸出編碼。 3.實施內容安全策略(CSP)限制腳本來源。通過這些策略,可以有效防護會話相關的XSS攻擊,確保用戶數據安全。

您如何優化PHP會話性能?您如何優化PHP會話性能?Apr 23, 2025 am 12:13 AM

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显著提升应用在高并发环境下的效率。

什麼是session.gc_maxlifetime配置設置?什麼是session.gc_maxlifetime配置設置?Apr 23, 2025 am 12:10 AM

theSession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceisesneededeededeedeedeededto toavoidperformance andunununununexpectedLogOgouts.3)

您如何在PHP中配置會話名?您如何在PHP中配置會話名?Apr 23, 2025 am 12:08 AM

在PHP中,可以使用session_name()函數配置會話名稱。具體步驟如下:1.使用session_name()函數設置會話名稱,例如session_name("my_session")。 2.在設置會話名稱後,調用session_start()啟動會話。配置會話名稱可以避免多應用間的會話數據衝突,並增強安全性,但需注意會話名稱的唯一性、安全性、長度和設置時機。

您應該多久再生一次會話ID?您應該多久再生一次會話ID?Apr 23, 2025 am 12:03 AM

會話ID應在登錄時、敏感操作前和每30分鐘定期重新生成。 1.登錄時重新生成會話ID可防會話固定攻擊。 2.敏感操作前重新生成提高安全性。 3.定期重新生成降低長期利用風險,但需權衡用戶體驗。

如何在PHP中設置會話cookie參數?如何在PHP中設置會話cookie參數?Apr 22, 2025 pm 05:33 PM

在PHP中設置會話cookie參數可以通過session_set_cookie_params()函數實現。 1)使用該函數設置參數,如過期時間、路徑、域名、安全標誌等;2)調用session_start()使參數生效;3)根據需求動態調整參數,如用戶登錄狀態;4)注意設置secure和httponly標誌以提升安全性。

在PHP中使用會議的主要目的是什麼?在PHP中使用會議的主要目的是什麼?Apr 22, 2025 pm 05:25 PM

在PHP中使用會話的主要目的是維護用戶在不同頁面之間的狀態。 1)會話通過session_start()函數啟動,創建唯一會話ID並存儲在用戶cookie中。 2)會話數據保存在服務器上,允許在不同請求間傳遞數據,如登錄狀態和購物車內容。

您如何在子域中分享會議?您如何在子域中分享會議?Apr 22, 2025 pm 05:21 PM

如何在子域名間共享會話?通過設置通用域名的會話cookie實現。 1.在服務器端設置會話cookie的域為.example.com。 2.選擇合適的會話存儲方式,如內存、數據庫或分佈式緩存。 3.通過cookie傳遞會話ID,服務器根據ID檢索和更新會話數據。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版