小程序支付+回调+查询订单记录

一丶查看官方开发文档,地址为:

链接: https://pay.weixin.qq.com/wiki/doc/api/index.html.

二丶回传前端发起支付的参数
/**
     *调用统一下单接口,并接受返回的结果
     * @param openid    用户唯一标识(详情请看微信登录)
     * @param money     金额
     * @param orderNum  订单号
     * @return
     */
     public static Map<String, String> getPayment(String openid,double money,String orderNum) throws Exception {

         //初始化返回值
         Map<String,String> payMap = new TreeMap<>();
         //获取32位随机字符串
         String nonce_str = PayUtil.getRandomStringByLength(32);
         //商品描述 这里是避免出现中文乱码
         String body = new String(WeChatConfigVo.body.getBytes(StandardCharsets.ISO_8859_1));
         //封装数据
         SortedMap<String,String> map = new TreeMap<>();
         map.put("appid", WeChatConfigVo.appId);
         map.put("mch_id", WeChatConfigVo.mch_id);
         map.put("nonce_str", nonce_str);
         map.put("body", body);
         map.put("out_trade_no", orderNum);                          //商户订单号,自己的订单ID
         map.put("total_fee", PayUtil.moneyToIntegerStr(money));  //支付金额,这边需要转成字符串类型,否则后面的签名会失败
         map.put("spbill_create_ip", IPUtils.getIpAddr());         //IP地址
         map.put("notify_url", WeChatConfigVo.notify_url);        //支付成功后的回调地址
         map.put("trade_type", WeChatConfigVo.tradeType);        //支付方式
         map.put("openid", openid);

         //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
         String preStr = PayUtil.createLinkString(map);
         //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
         String mySign = PayUtil.sign(preStr, WeChatConfigVo.key, "utf-8").toUpperCase();
         //放到map集合
         map.put("sign",mySign);
         //装换为xml
         String mapXML = PayUtil.mapToXml(map);
         //调用统一下单接口,并接受返回的结果
         String result = PayUtil.httpRequest(WeChatConfigVo.pay_url, "POST", mapXML);
         //解析xml返回map集合
         Map mapResult = PayUtil.xmlToMap(result);
         //获取返回状态码和返回结果
         String return_code = (String) mapResult.get("return_code");
         String result_code = (String) mapResult.get("result_code");
         //判断是否成功
         if("SUCCESS".equals(return_code) || return_code.equals(result_code)){
             //成功返回预订单ID
             String prepay_id = (String) mapResult.get("prepay_id");
             //获取当前时间戳
             String timeStamp = PayUtil.getCurrentTimeStamp();
             //封装返回结果
             payMap.put("nonceStr", nonce_str);
             payMap.put("package", prepay_id);
             //要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
             payMap.put("timeStamp", timeStamp);
             //拼接签名需要的参数
             String stringSignTemp = "appId=" + WeChatConfigVo.appId + "&nonceStr=" + nonce_str + "&package=prepay_id="
                     + prepay_id + "&signType=" + WeChatConfigVo.signType + "&timeStamp=" + timeStamp;
             //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
             String paySign = PayUtil.sign(stringSignTemp, WeChatConfigVo.key, "utf-8").toUpperCase();
             //封装到map集合
             payMap.put("paySign",paySign);
         }else {
             throw new ServiceException("支付失败");
         }
         payMap.put("appId", WeChatConfigVo.appId);
         //数据返回
         return payMap;
     }
3丶支付后会以流的方式回传数据(回调函数),生命回调函数是要以接口的形式编写,并在外网上可访问,而且接口不可携带参数
@Override
    public void wxNotify(HttpServletRequest request, HttpServletResponse response) {

        log.info("================回调正在执行===============");
        try {
            //获取缓冲流
            BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String line ;
            //获取StringBuilder对象
            StringBuilder sb = new StringBuilder();
            //拼接字符
            while ((line = br.readLine()) != null){
                sb.append(line);
            }
            br.close();
            //sb为微信返回的xml
            String notifyXml = sb.toString();
            //将xml转换为map对象
            Map<String,String> map = PayUtil.xmlToMap(notifyXml);
            log.info("===============准备读取=================");
            //获取return_code值 作为判断条件
            String returnCode = map.get("return_code");
            log.info("=====参数返回:======="+""+returnCode+""+"===============");
            //String resultCode = map.get("result_code");
            String preXml ;
            //成功返回
            if("SUCCESS".equals(returnCode)){

                //判断验证签名是否正确
                log.info("==============进来了没-===================");
                //判断签名是否正确
                boolean flag = PayUtil.isSignatureValid(map, WeChatConfigVo.key);
                //如果正确进行下一步操作
                if(flag){
                    //订单号
                    String orderNum = map.get("out_trade_no");
                    //金额
                    String money = map.get("total_fee");
                    log.info("=====钱:===="+money+"=============");
                    //交易时间
                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
在 ThinkPHP6 框架下集成微信小程序支付功能,并处理支付完成后的回调逻辑,主要涉及以下几个步骤: ### 1. 配置微信支付相关参数 首先需要配置微信支付相关的参数,包括 `appid`、`mch_id`(商户号)、`key`(API密钥)等信息。这些参数通常存储在配置文件中,以便后续调用。 ```php // config/wechat_pay.php return [ 'appid' => '你的小程序AppID', 'mch_id' => '你的商户号', 'key' => '你的API密钥', 'notify_url' => 'https://yourdomain.com/api/v1/pay/notify', // 支付结果通知地址 'ssl_key' => './cert/apiclient_key.pem', // 微信支付异步通知数据签名验证证书路径 'ssl_cert' => './cert/apiclient_cert.pem' ]; ``` ### 2. 统一下单接口(生成预支付订单) 用户发起支付请求后,服务器需调用微信统一下单接口获取 `prepay_id`,并构造小程序端可使用的支付参数。 ```php use think\facade\Config; public function getPreOrder() { $config = Config::get('wechat_pay'); $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $nonceStr = uniqid(); $body = '商品描述'; $outTradeNo = date('YmdHis') . rand(1000, 9999); $totalFee = 1; // 单位:分 $spbillCreateIp = request()->ip(); $notifyUrl = $config['notify_url']; $tradeType = 'JSAPI'; // 小程序为 'JSAPI' $postData = [ 'appid' => $config['appid'], 'mch_id' => $config['mch_id'], 'nonce_str' => $nonceStr, 'body' => $body, 'out_trade_no' => $outTradeNo, 'total_fee' => $totalFee, 'spbill_create_ip' => $spbillCreateIp, 'notify_url' => $notifyUrl, 'trade_type' => $tradeType, ]; // 签名生成 ksort($postData); $stringA = http_build_query($postData); $stringSignTemp = $stringA . '&key=' . $config['key']; $sign = strtoupper(md5($stringSignTemp)); $postData['sign'] = $sign; // 发送POST请求到微信统一下单接口 $xml = new \SimpleXMLElement('<xml/>'); foreach ($postData as $key => $val) { $xml->addChild($key, $val); } $response = curl_post($url, $xml->asXML()); // 自定义curl函数发送请求 // 解析返回的XML数据 $result = simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA); if ($result && $result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') { $prepayId = (string)$result->prepay_id; $timeStamp = strval(time()); $nonceStr = uniqid(); $package = "prepay_id={$prepayId}"; $signType = 'MD5'; // 再次签名用于小程序调起支付 $paySignData = [ 'appId' => $config['appid'], 'timestamp' => $timeStamp, 'nonceStr' => $nonceStr, 'package' => $package, 'signType' => $signType ]; ksort($paySignData); $stringSignTemp = http_build_query($paySignData) . '&key=' . $config['key']; $paySign = strtoupper(md5($stringSignTemp)); return json([ 'timeStamp' => $timeStamp, 'nonceStr' => $nonceStr, 'package' => $package, 'signType' => $signType, 'paySign' => $paySign ]); } else { return json(['error' => '下单失败']); } } ``` ### 3. 处理支付回调通知 当用户完成支付后,微信会向指定的 `notify_url` 发送支付结果通知。此时需要验证签名,并更新订单状态。 ```php public function receiveNotify() { $config = Config::get('wechat_pay'); // 获取原始输入流数据 $input = file_get_contents("php://input"); $data = json_decode(json_encode(simplexml_load_string($input, 'SimpleXMLElement', LIBXML_NOCDATA)), true); // 验证签名是否合法 $sign = $data['sign']; unset($data['sign']); ksort($data); $stringA = http_build_query($data); $stringSignTemp = $stringA . '&key=' . $config['key']; $trueSign = strtoupper(md5($stringSignTemp)); if ($sign != $trueSign) { echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; exit; } // 判断交易是否成功 if ($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') { $transactionId = $data['transaction_id']; // 微信交易流水号 $outTradeNo = $data['out_trade_no']; // 商户订单号 $totalFee = $data['total_fee']; // 支付金额(单位:分) // 查询本地订单是否存在 $order = Db::name('orders')->where('order_no', $outTradeNo)->find(); if (!$order) { echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[订单不存在]]></return_msg></xml>'; exit; } // 校验金额是否一致 if ($order['amount'] * 100 != $totalFee) { // 假设数据库金额单位是元 echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[金额不一致]]></return_msg></xml>'; exit; } // 更新订单状态为已支付 Db::name('orders')->where('order_no', $outTradeNo)->update(['status' => 1]); // 返回成功应答 echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; exit; } else { echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>'; exit; } } ``` ### 4. 路由设置 在路由文件中定义支付下单和回调处理的接口路径。 ```php // route/api.php use think\facade\Route; Route::post('api/v1/pay/pre_order', 'app\controller\PayController/getPreOrder'); Route::post('api/v1/pay/notify', 'app\controller\PayController/receiveNotify'); ``` ### 5. 注意事项与安全建议 - **签名验证**:必须对微信回调的通知数据进行签名验证,防止伪造请求。 - **幂等性处理**:同一个通知可能会被多次发送,因此在处理时要保证幂等性,避免重复处理订单。 - **日志记录**:建议将每次回调的数据记录到日志中,便于排问题。 - **HTTPS支持**:回调地址必须使用 HTTPS 协议,且证书有效。 通过上述流程,可以在 ThinkPHP6 中实现完整的微信小程序支付功能,并确保支付回调的安全性和可靠性[^3]。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值