微信公众号支付详细过程

背景

公司的公众号有个保险项目需要用到微信支付,官网java文档写的并不是很详细。个人根据网上一些demo总结出来的微信支付功能,中间遇到过几个坑,后续会详细讲解。话不多说,先上代码!

一.转换MD5格式类MD5Util类

import java.security.MessageDigest;

public class MD5Util {
	public final static String MD5(String s) {  
        char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};         
  
        try {  
            byte[] btInput = s.getBytes();   
            MessageDigest mdInst = MessageDigest.getInstance("MD5");  
            mdInst.update(btInput);   
            byte[] md = mdInst.digest();  
            int j = md.length;  
            char str[] = new char[j * 2];  
            int k = 0;  
            for (int i = 0; i < j; i++) {  
                byte byte0 = md[i];  
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];  
                str[k++] = hexDigits[byte0 & 0xf];  
            }  
            String md5Str = new String(str);   
            return md5Str;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}

二.统一下单参数类UnifiedOrderRequest

public class UnifiedOrderRequest {
	 private String appid;// 公众账号ID   是      String(32)  wxd678efh567hg6787  微信支付分配的公众账号ID(企业号corpid即为此appId)  
	    private String mch_id;//商户号  必填 String(32)  1230000109  微信支付分配的商户号  
	    private String device_info; //设备号   否       String(32)  013467007045764 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"  
	    private String nonce_str;//随机字符串    是        String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,长度要求在32位以内。推荐随机数生成算法  
	    private String sign;//签名    是        String(32)   C380BEC2BFD727A4B6845133519F3AD6  通过签名算法计算得出的签名值,详见签名生成算法  
	    private String sign_type;//签名类型 sign_type   否   String(32)  HMAC-SHA256 签名类型,默认为MD5,支持HMAC-SHA256和MD5。  
	    private String body;//商品描述  body 是   String(128)    腾讯充值中心-QQ会员充值  商品简单描述,该字段请按照规范传递,具体请见参数规定     
	    private String detail;//商品详情    detail  否   String(6000)        单品优惠字段(暂未上线)  
	    private String attach;//附加数据    attach  否   String(127) 深圳分店    附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。  
	    private String out_trade_no;//商户订单号 out_trade_no    是   String(32)  20150806125346  商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号  
	    private String fee_type;//标价币种  fee_type    否   String(16)  CNY 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型  
	    private String total_fee;//标价金额 total_fee   是   Int 88  订单总金额,单位为分,详见支付金额  
	    private String spbill_create_ip;//终端IP  spbill_create_ip    是   String(16)  123.12.12.123   APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。  
	    private String time_start;//交易起始时间  time_start  否   String(14)  20091225091010  订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则  
	    private String time_expire;//交易结束时间 time_expire 否   String(14)  20091227091010  订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则   注意:最短失效时间间隔必须大于5分钟  
	    private String goods_tag;//订单优惠标记   goods_tag   否   String(32)  WXG 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠  
	    private String notify_url;//通知地址    notify_url  是   String(256) http://www.weixin.qq.com/wxpay/pay.php  异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。  
	    private String trade_type;//交易类型    trade_type  是   String(16)  JSAPI   取值如下:JSAPI,NATIVE,APP等,说明详见参数规定  
	    private String product_id;//商品ID    product_id  否   String(32)  12235413214070356458058 trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。  
	    private String limit_pay;//指定支付方式   limit_pay   否   String(32)  no_credit   上传此参数no_credit--可限制用户不能使用信用卡支付  
	    private String openid;//用户标识    openid  否   String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o    trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换
//以下省略get和set方法
}		

三.统一下单返回参数类UnifiedOrderRespose


public class UnifiedOrderRespose {
	private String return_code;             //返回状态码  
    private String return_msg;              //返回信息  
    private String appid;                   //公众账号ID  
    private String mch_id;                  //商户号  
    private String device_info;             //设备号  
    private String nonce_str;               //随机字符串  
    private String sign;                    //签名  
    private String result_code;             //业务结果  
    private String err_code;                //错误代码  
    private String err_code_des;            //错误代码描述  
    private String trade_type;              //交易类型  
    private String prepay_id;               //预支付交易会话标识  
    private String code_url;                //二维码链接
//....以下省略set和get方法
}	

四.将返回结果转换为json类WXAuthUtil,该类部分微信参数,其中MCH_ID、APPID、APPSECRET在微信公众号设置,KEY在微信商户号上设置,KEY用于后面生成签名。

import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class WXAuthUtil {
    public static final String MCH_ID="";//商户号
    public static final String APPID="";//
    public static final String APPSECRET ="";
    public static final String KEY="";
    public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
        JSONObject jsonObject =null;
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet httpGet =new HttpGet(url);
        HttpResponse response =  client.execute(httpGet);
        HttpEntity entity =response.getEntity();
        if(entity!=null)
        {
            //把返回的结果转换为JSON对象
            String result =EntityUtils.toString(entity, "UTF-8");
            jsonObject =JSON.parseObject(result);
        }
        
        return jsonObject;
    }
}

五.签名方式类WXPayConstants,包含微信返回结果定义的参数

public class WXPayConstants {
	public enum SignType {  
        MD5, HMACSHA256  
    }  
    public static final String FAIL     = "FAIL";  
    public static final String SUCCESS  = "SUCCESS";  
    public static final String HMACSHA256 = "HMAC-SHA256";  
    public static final String MD5 = "MD5";  
    public static final String FIELD_SIGN = "sign";  
    public static final String FIELD_SIGN_TYPE = "sign_type";
}

六.请求连接类HttpKit,统一下单、查询订单用到

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;

/** 
 * https 请求 微信为https的请求 
 * 
 * @author andy 
 * @date 2015-10-9 下午2:40:19 
 */   
public class HttpKit {  
    private static final String DEFAULT_CHARSET = "UTF-8";  
    /** 
     * @return 返回类型: 
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     * @throws NoSuchProviderException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyManagementException 
     * @description 功能描述: get 请求 
     */  
    public static String get(String url, Map<String, String> params, Map<String, String> headers) throws IOException, ExecutionException, InterruptedException {  
        AsyncHttpClient http = new AsyncHttpClient();  
        AsyncHttpClient.BoundRequestBuilder builder = http.prepareGet(url);  
        builder.setBodyEncoding(DEFAULT_CHARSET);  
        if (params != null && !params.isEmpty()) {  
            Set<String> keys = params.keySet();  
            for (String key : keys) {  
                //builder.addQueryParame(key, params.get(key));
                builder.addQueryParam(key, params.get(key));
            }  
        }  
  
        if (headers != null && !headers.isEmpty()) {  
            Set<String> keys = headers.keySet();  
            for (String key : keys) {  
                builder.addHeader(key, params.get(key));  
            }  
        }  
        Future<Response> f = builder.execute();  
        String body = f.get().getResponseBody(DEFAULT_CHARSET);  
        http.close();  
        return body;  
    }  
  
    /** 
     * @return 返回类型: 
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     * @throws NoSuchProviderException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyManagementException 
     * @description 功能描述: get 请求 
     */  
    public static String get(String url) throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, UnsupportedEncodingException, IOException, ExecutionException, InterruptedException {  
        return get(url, null);  
    }  
  
    /** 
     * @return 返回类型: 
     * @throws IOException 
     * @throws NoSuchProviderException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyManagementException 
     * @throws UnsupportedEncodingException 
     * @description 功能描述: get 请求 
     */  
    public static String get(String url, Map<String, String> params) throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, UnsupportedEncodingException, IOException, ExecutionException, InterruptedException {  
        return get(url, params, null);  
    }  
  
    /** 
     * @return 返回类型: 
     * @throws IOException 
     * @throws NoSuchProviderException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyManagementException 
     * @description 功能描述: POST 请求 
     */  
    public static String post(String url, Map<String, String> params) throws IOException, ExecutionException, InterruptedException {  
        AsyncHttpClient http = new AsyncHttpClient();  
        AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(url);  
        builder.setBodyEncoding(DEFAULT_CHARSET);  
        if (params != null && !params.isEmpty()) {  
            Set<String> keys = params.keySet();  
            for (String key : keys) {  
                //builder.addParameter(key, params.get(key));
                builder.addQueryParam(key, params.get(key));
            }  
        }  
        Future<Response> f = builder.execute();  
        String body = f.get().getResponseBody(DEFAULT_CHARSET);  
        http.close();  
        return body;  
    }  
  
    public static String post(String url, String s) throws IOException, ExecutionException, InterruptedException {  
        AsyncHttpClient http = new AsyncHttpClient();  
        AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(url);  
        builder.setBodyEncoding(DEFAULT_CHARSET);  
        builder.setBody(s);  
        Future<Response> f = builder.execute();  
        String body = f.get().getResponseBody(DEFAULT_CHARSET);  
        http.close();  
        return body;  
    }  
      
}  

七.支付工具类

import java.io.BufferedOutputStream;  
import java.io.BufferedReader;  
import java.io.ByteArrayInputStream;  
import java.io.InputStream;  
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;  
import java.io.Writer;  
import java.net.HttpURLConnection;  
import java.net.URL;  
import java.util.*;  
import java.security.MessageDigest;  
import org.w3c.dom.Node;  
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.rybsj.entity.PaymentOrder;
import com.rybsj.tools.WXPayConstants.SignType;
import com.thoughtworks.xstream.XStream;  
import com.thoughtworks.xstream.core.util.QuickWriter;  
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;  
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;  
import com.thoughtworks.xstream.io.xml.XppDriver;

import javax.crypto.Mac;  
import javax.crypto.spec.SecretKeySpec;  
import javax.xml.parsers.DocumentBuilder;  
import javax.xml.parsers.DocumentBuilderFactory;  
import javax.xml.transform.OutputKeys;  
import javax.xml.transform.Transformer;  
import javax.xml.transform.TransformerFactory;  
import javax.xml.transform.dom.DOMSource;  
import javax.xml.transform.stream.StreamResult;    
import org.jdom.Document;  
import org.jdom.Element;  
import org.jdom.input.SAXBuilder;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;

/** 
 * 支付工具类 
 * @author lh 
 */
public class WXPayUtil {
	public static Logger log=LoggerFactory.getLogger(WXPayUtil.class);
	 /** 
     * 生成订单对象信息 
     * @param orderId 订单号 
     * @param appId 微信appId 
     * @param mch_id 微信分配的商户ID 
     * @param body  支付介绍主体 
     * @param price 支付价格(放大100倍) 
     * @param spbill_create_ip 终端IP 
     * @param notify_url  异步直接结果通知接口地址 
     * @param noncestr  
     * @return 
     */  
    public static Map<String,Object> createOrderInfo(Map<String, String> requestMap) {    
        //生成订单对象    
        UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();    
        unifiedOrderRequest.setAppid(requestMap.get("appId"));//公众账号ID    
        unifiedOrderRequest.setBody(requestMap.get("body"));
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值