利用支付宝的团队编写的 HttpProtocolHandler 、StringRequestEntity 调用微信 支付引发的 body不是UTF8编码的

微信支付调用问题:HttpProtocolHandler UTF8编码异常
博客讲述了在使用支付宝团队编写的HttpProtocolHandler和StringRequestEntity进行微信支付调用时遇到的body编码问题。尽管代码经过修改以支持POST请求的body,但在实际调用微信支付API时,发现body内容并非UTF8编码,而在Main中发送请求则无此问题。


HttpProtocolHandler 代码修改后的代码如下


package com.whb.common.util.httpclient;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
import org.apache.http.entity.StringEntity;
import org.apache.log4j.Logger;

import com.whb.common.util.Util;

/* *
 *类名:HttpProtocolHandler
 *功能:HttpClient方式访问
 *详细:获取远程HTTP数据
 *版本:3.3
 *日期:2012-08-17
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class HttpProtocolHandler {

	private static Logger log = Logger.getLogger(HttpProtocolHandler.class); 
	
    private static String              DEFAULT_CHARSET                     = "utf-8";

    /** 连接超时时间,由bean factory设置,缺省为8秒钟 */
    private int                        defaultConnectionTimeout            = 8000;

    /** 回应超时时间, 由bean factory设置,缺省为30秒钟 */
    private int                        defaultSoTimeout                    = 30000;

    /** 闲置连接超时时间, 由bean factory设置,缺省为60秒钟 */
    private int                        defaultIdleConnTimeout              = 60000;

    private int                        defaultMaxConnPerHost               = 30;

    private int                        defaultMaxTotalConn                 = 80;

    /** 默认等待HttpConnectionManager返回连接超时(只有在达到最大连接数时起作用):1秒*/
    private static final long          defaultHttpConnectionManagerTimeout = 3 * 1000;

    /**
     * HTTP连接管理器,该连接管理器必须是线程安全的.
     */
    private HttpConnectionManager      connectionManager;

    private static HttpProtocolHandler httpProtocolHandler                 = new HttpProtocolHandler();

    /**
     * 工厂方法
     * 
     * @return
     */
    public static HttpProtocolHandler getInstance() {
        return httpProtocolHandler;
    }

    /**
     * 私有的构造方法
     */
    private HttpProtocolHandler() {
        // 创建一个线程安全的HTTP连接池
        connectionManager = new MultiThreadedHttpConnectionManager();
        connectionManager.getParams().setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);
        connectionManager.getParams().setMaxTotalConnections(defaultMaxTotalConn);
        IdleConnectionTimeoutThread ict = new IdleConnectionTimeoutThread();
        ict.addConnectionManager(connectionManager);
        ict.setConnectionTimeout(defaultIdleConnTimeout);

        ict.start();
        
    }

    /**
     * 执行Http请求
     * 
     * @param request 请求数据
     * @param strParaFileName 文件类型的参数名
     * @param strFilePath 文件路径
     * @return 
     * @throws HttpException, IOException 
     */
    public HttpResponse execute(HttpRequest request, String strParaFileName, String strFilePath) throws HttpException, IOException {
        HttpClient httpclient = new HttpClient(connectionManager);       
        
        // 设置连接超时
        int connectionTimeout = defaultConnectionTimeout;
        if (request.getConnectionTimeout() > 0) {
            connectionTimeout = request.getConnectionTimeout();
        }
        httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);

        // 设置回应超时
        int soTimeout = defaultSoTimeout;
        if (request.getTimeout() > 0) {
            soTimeout = request.getTimeout();
        }
        httpclient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);

        // 设置等待ConnectionManager释放connection的时间
        httpclient.getParams().setConnectionManagerTimeout(defaultHttpConnectionManagerTimeout);

        String charset = request.getCharset();
        charset = charset == null ? DEFAULT_CHARSET : charset;
        HttpMethod method = null;

        //get模式且不带上传文件
        if (request.getMethod().equals(HttpRequest.METHOD_GET)) {
            method = new GetMethod(request.getUrl());
            method.getParams().setCredentialCharset(charset);

            // parseNotifyConfig会保证使用GET方法时,request一定使用QueryString
            method.setQueryString(request.getQueryString());
        } else if(strParaFileName.equals("") && strFilePath.equals("")) {
        	//post模式且不带上传文件
            method = new PostMethod(request.getUrl());
            
            ((PostMethod) method).addParameters(request.getParameters());
            method.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; text/html; charset=" + charset);
            
            
            String body=request.getBody();
            
            /**
             * 如果没有这样设置 默认编码格式是 ISO-8859-1 向微信发送请求会有问题
             */
            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body,"text/html",charset));
        }
        else {
        	//post模式且带上传文件
            method = new PostMethod(request.getUrl());
            List<Part> parts = new ArrayList<Part>();
            for (int i = 0; i < request.getParameters().length; i++) {
            	parts.add(new StringPart(request.getParameters()[i].getName(), request.getParameters()[i].getValue(), charset));
            }
            //增加文件参数,strParaFileName是参数名,使用本地文件
            parts.add(new FilePart(strParaFileName, new FilePartSource(new File(strFilePath))));
            
            // 设置请求体
            ((PostMethod) method).setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[0]), new HttpMethodParams()));
        }

        // 设置Http Header中的User-Agent属性
        method.addRequestHeader("User-Agent", "Mozilla/4.0");
        HttpResponse response = new HttpResponse();

        try {
            httpclient.executeMethod(method);
            if (request.getResultType().equals(HttpResultType.STRING)) {
                response.setStringResult(method.getResponseBodyAsString());
            } else if (request.getResultType().equals(HttpResultType.BYTES)) {
                response.setByteResult(method.getResponseBody());
            }
            response.setResponseHeaders(method.getResponseHeaders());
        } catch (UnknownHostException ex) {

            return null;
        } catch (IOException ex) {

            return null;
        } catch (Exception ex) {

            return null;
        } finally {
            method.releaseConnection();
        }
        return response;
    }

    /**
     * 将NameValuePairs数组转变为字符串
     * 
     * @param nameValues
     * @return
     */
    protected String toString(NameValuePair[] nameValues) {
        if (nameValues == null || nameValues.length == 0) {
            return "null";
        }

        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < nameValues.length; i++) {
            NameValuePair nameValue = nameValues[i];

            if (i == 0) {
                buffer.append(nameValue.getName() + "=" + nameValue.getValue());
            } else {
                buffer.append("&" + nameValue.getName() + "=" + nameValue.getValue());
            }
        }

        return buffer.toString();
    }
}



修改过的代码:

原先不支持 Post for body 后来 我加了 如下 ,如下代码 会引发微信 支付引发的 body不是UTF8编码的,但如果用Main发送请求是正常的


            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body));

但对 StringRequestEntity 用的不多,后来修改如下:


            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body,"text/html",charset));




以下是 Python 调用微信支付的基本流程: 1. 创建订单:首先需要在微信支付后台创建订单并获取到订单号。 2. 生成签名:根据微信支付接口文档要求,需要对订单信息进行签名加密。 3. 统一下单:调用微信支付接口,将订单信息提交给微信支付后台,获取到预支付交易会话标识 prepay_id。 4. 生成支付参数:根据微信支付接口文档要求,生成支付参数并返回给前端。 5. 调起支付:前端使用生成的支付参数发起支付请求。 下面是一个简单的 Python 程序示例,用于调用微信支付: ```python import requests import hashlib import time import xml.etree.ElementTree as ET # 微信支付接口地址 url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信支付相关信息 appid = '微信公众号appid' mch_id = '商户号' key = '商户支付密钥' body = '订单描述' out_trade_no = '商户订单号' total_fee = '订单金额,单位为分' notify_url = '支付成功后的回调地址' trade_type = 'JSAPI' openid = '用户openid' # 生成签名 timestamp = str(int(time.time())) nonce_str = 'wechat_pay' stringA = 'appid=' + appid + '&body=' + body + '&mch_id=' + mch_id + '&nonce_str=' + nonce_str + '&notify_url=' + notify_url + '&openid=' + openid + '&out_trade_no=' + out_trade_no + '&total_fee=' + total_fee + '&trade_type=' + trade_type + '&key=' + key sign = hashlib.md5(stringA.encode('utf-8')).hexdigest().upper() # 生成支付参数 xml_data = '<xml>' xml_data += '<appid>' + appid + '</appid>' xml_data += '<body>' + body + '</body>' xml_data += '<mch_id>' + mch_id + '</mch_id>' xml_data += '<nonce_str>' + nonce_str + '</nonce_str>' xml_data += '<notify_url>' + notify_url + '</notify_url>' xml_data += '<openid>' + openid + '</openid>' xml_data += '<out_trade_no>' + out_trade_no + '</out_trade_no>' xml_data += '<total_fee>' + total_fee + '</total_fee>' xml_data += '<trade_type>' + trade_type + '</trade_type>' xml_data += '<sign>' + sign + '</sign>' xml_data += '</xml>' # 调用微信支付接口 response = requests.post(url, data=xml_data.encode('utf-8')) response.encoding = 'utf-8' # 解析返回结果 tree = ET.fromstring(response.text) prepay_id = tree.find('prepay_id').text # 生成支付参数 timestamp = str(int(time.time())) nonce_str = 'wechat_pay' stringA = 'appId=' + appid + '&nonceStr=' + nonce_str + '&package=prepay_id=' + prepay_id + '&signType=MD5' + '&timeStamp=' + timestamp + '&key=' + key pay_sign = hashlib.md5(stringA.encode('utf-8')).hexdigest().upper() pay_param = { 'appId': appid, 'timeStamp': timestamp, 'nonceStr': nonce_str, 'package': 'prepay_id=' + prepay_id, 'signType': 'MD5', 'paySign': pay_sign } # 返回支付参数 print(pay_param) ``` 以上代码仅供参考,实际调用微信支付时还需要根据接口文档和具体业务需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值