关于微信支付(一)
微信支付有两种方式
1.第三方网站弹出二维码客户扫描支付
2.在微信内部H5直接发起支付
这篇文章先讲 第一种最常用的 弹出二维码支付
二维码支付
大概流程如下
用户选择支付->后台程序创建订单->请求微信同意下单接口(除了常规参数还需要一个接口这个URL是用户支付完成后通知你的接口)->提交成功后 微信返回参数其中有一个链接,这个链接转成二维码 用户扫描就可以支付了
此时支付流程已经结束
如果用户支付了 微信就会调用你的URL 通知你,你需要返回给微信 一个信息(大概意思就是 告诉微信我知道了) 不然微信会一直 请求你
<!--more-->
准备工作
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
参数:
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
公众账号ID | appid | 是 | String(32) | wxd678efh567hg6787 | 微信支付分配的公众账号ID(企业号corpid即为此appId) |
商户号 | mch_id | 是 | String(32) | 1230000109 | 微信支付分配的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB" |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,长度要求在32位以内。推荐随机数生成算法 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 通过签名算法计算得出的签名值,详见签名生成算法 |
签名类型 | sign_type | 否 | String(32) | MD5 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 |
商品描述 | body | 是 | String(128) | 腾讯充值中心-QQ会员充值 | 商品简单描述,该字段请按照规范传递,具体请见参数规定 |
商品详情 | detail | 否 | String(6000) | 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明” | |
附加数据 | attach | 否 | String(127) | 深圳分店 | 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 |
商户订单号 | out_trade_no | 是 | String(32) | 20150806125346 | 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号 |
标价币种 | fee_type | 否 | String(16) | CNY | 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型 |
标价金额 | total_fee | 是 | Int | 88 | 订单总金额,单位为分,详见支付金额 |
终端IP | spbill_create_ip | 是 | String(16) | 123.12.12.123 | APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 |
交易起始时间 | time_start | 否 | String(14) | 20091225091010 | 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 |
交易结束时间 | time_expire | 否 | String(14) | 20091227091010 | 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则建议:最短失效时间间隔大于1分钟 |
订单优惠标记 | goods_tag | 否 | String(32) | WXG | 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠 |
通知地址 | notify_url | 是 | String(256) | 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 | |
交易类型 | trade_type | 是 | String(16) | JSAPI | JSAPI 公众号支付NATIVE 扫码支付APP APP支付说明详见参数规定 |
商品ID | product_id | 否 | String(32) | 12235413214070356458058 | trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。 |
指定支付方式 | limit_pay | 否 | String(32) | no_credit | 上传此参数no_credit--可限制用户不能使用信用卡支付 |
用户标识 | openid | 否 | String(128) | oUpF8uMuAJO_M2pxb1Q9zNjWeS6o | trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换 |
-场景信息 | scene_info | 否 | String(256) | {"store_info" : {"id": "SZTX001","name": "腾大餐厅","area_code": "440305","address": "科技园中一路腾讯大厦" }} | 该字段用于上报场景信息,目前支持上报实际门店信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }} ,字段详细说明请点击行前的+展开 |
-门店id | id | 否 | String(32) | SZTX001 | 门店唯一标识 |
-门店名称 | name | 否 | String(64) | 腾讯大厦腾大餐厅 | 门店名称 |
-门店行政区划码 | area_code | 否 | String(6) | 440305 | 门店所在地行政区划码,详细见《最新县及县以上行政区划代码》 |
-门店详细地址 | address | 否 | String(128) | 科技园中一路腾讯大厦 | 门店详细地址 |
举例如下:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付测试</attach>
<body>JSAPI支付测试</body>
<mch_id>10000100</mch_id>
<detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
这里 我发现请求参数太多了 所以我把所有请求参数 返回参数都做成了对象,首先是微信同意下单 参数接口
/**
* 统一下单请求参数(必填)
* @author An
*
*/
public class UnifiedOrderRequest {
private String appid; //公众账号ID
private String mch_id; //商户号
private String nonce_str; //随机字符串
private String sign; //签名
private String body; //商品描述
private String out_trade_no; //商户订单号
private String total_fee; //总金额
private String spbill_create_ip; //终端IP
private String notify_url; //通知地址
private String trade_type; //交易类型
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
}
/**
* 统一下单请求参数(非必填)
* @author An
*
*/
public class UnifiedOrderRequestExt extends UnifiedOrderRequest{
private String device_info; //设备号
private String detail; //商品详情
private String attach; //附加数据
private String fee_type; //货币类型
private String time_start; //交易起始时间
private String time_expire; //交易结束时间
private String goods_tag; //商品标记
private String product_id; //商品ID
private String limit_pay; //指定支付方式
private String openid; //用户标识
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getTime_start() {
return time_start;
}
public void setTime_start(String time_start) {
this.time_start = time_start;
}
public String getTime_expire() {
return time_expire;
}
public void setTime_expire(String time_expire) {
this.time_expire = time_expire;
}
public String getGoods_tag() {
return goods_tag;
}
public void setGoods_tag(String goods_tag) {
this.goods_tag = goods_tag;
}
public String getProduct_id() {
return product_id;
}
public void setProduct_id(String product_id) {
this.product_id = product_id;
}
public String getLimit_pay() {
return limit_pay;
}
public void setLimit_pay(String limit_pay) {
this.limit_pay = limit_pay;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
}
准备两个工具类
1.MD5加密工具类
public class WeChatMD5{
/**
*
* @see MD5加密
* @param origin 被加密字符串
* @param input_charset 字符串编码方式
* @return
*/
public static String MD5Encode(String origin,String input_charset){
return DigestUtils.md5Hex(getContentBytes(origin, input_charset));
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String input_charset) {
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
if(mysign.equals(sign)) {
return true;
}
else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws SignatureException
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
}
2.微信支付工具类
public class WeChatPayUtil {
/**
* 生成随机字符串
* @param length 长度
* @return
*/
public static String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 统一下单生成签名
* @param unifiedOrderRequest
* @return
*/
public static String createSign(UnifiedOrderRequestExt unifiedOrderRequest) {
//根据规则创建可排序的map集合
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", unifiedOrderRequest.getAppid());
packageParams.put("body", unifiedOrderRequest.getBody());
packageParams.put("mch_id", unifiedOrderRequest.getMch_id());
packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());
packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());
packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());
packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());
packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());
packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());
packageParams.put("attach", unifiedOrderRequest.getAttach());
if(unifiedOrderRequest.getOpenid()!=null){
packageParams.put("openid",unifiedOrderRequest.getOpenid());
}
StringBuffer sb = new StringBuffer();
Set<Entry<String, String>> es = packageParams.entrySet();//字典序
Iterator<Entry<String, String>> it = es.iterator();
while (it.hasNext()) {
Entry<String, String> entry = (Entry<String, String>) it.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
//为空不参与签名、参数名区分大小写
if (null != value && !"".equals(value) && !"sign".equals(key)
&& !"key".equals(key)) {
sb.append(key + "=" + value + "&");
}
}
//微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
String key=Config.getStringValue("key");
System.out.println(packageParams);
sb.append("key=" +key);
String sign = WeChatMD5.MD5Encode(sb.toString(), "utf-8") .toUpperCase();//MD5加密
System.out.println(sign);
return sign;
}
public static String createSign(UnifiedOrderSelect unifiedOrderRequest) {
//根据规则创建可排序的map集合
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", unifiedOrderRequest.getAppid());
packageParams.put("mch_id", unifiedOrderRequest.getMch_id());
packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());
packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());
StringBuffer sb = new StringBuffer();
Set<Entry<String, String>> es = packageParams.entrySet();//字典序
Iterator<Entry<String, String>> it = es.iterator();
while (it.hasNext()) {
Entry<String, String> entry = (Entry<String, String>) it.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
//为空不参与签名、参数名区分大小写
if (null != value && !"".equals(value) && !"sign".equals(key)
&& !"key".equals(key)) {
sb.append(key + "=" + value + "&");
}
}
//微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
String key=Config.getStringValue("key");
System.out.println(packageParams);
sb.append("key=" +key);
String sign = WeChatMD5.MD5Encode(sb.toString(), "utf-8") .toUpperCase();//MD5加密
System.out.println(sign);
return sign;
}
}
其中有一个生成随机字符串的 函数还有一个签名的函数,签名一会用到在讲
@Component
public class WeChatPay {
static Log log =LogFactory.getLog(WeChatPay.class);
//同意下单API
public static final String PAYURL="https://api.mch.weixin.qq.com/pay/unifiedorder";
//查询订单API
public static final String SELECT="https://api.mch.weixin.qq.com/pay/orderquery";
public static void main(String[] args) {
}
/**
* 生成订单方法
* @param body 订单详情
* @param orderId 订单号
* @param money 价格/元
* @param ip 用户端IP
* @return
*/
public String createOrderInfo(String body,String orderId,float money,String key,String tradeType,String openid) {
String ip="";
//生成订单对象
String resule="";
try {
ip = InetAddress.getLocalHost().getHostAddress().toString();
log.info("=.=生成订单开始(body="+body+",orderId="+orderId+",money="+money+",ip="+ip+")=.=");
String appid=Config.getStringValue("appID");
String mchId=Config.getStringValue("mch_id");
UnifiedOrderRequestExt unifiedOrderRequest = new UnifiedOrderRequestExt();
unifiedOrderRequest.setAppid(appid);//公众账号ID
unifiedOrderRequest.setMch_id(mchId);//商户号
if(openid!=null){
unifiedOrderRequest.setOpenid(openid);
}
unifiedOrderRequest.setAttach(orderId+","+key);//自定义参数 通知的时候修改成功和支付时间
unifiedOrderRequest.setNonce_str(WeChatPayUtil.getRandomString(11));//随机字符串
unifiedOrderRequest.setBody(body);//商品描述
unifiedOrderRequest.setOut_trade_no(orderId);//商户订单号
Float f = new Float(money*100);
int money1 = f.intValue();
unifiedOrderRequest.setTotal_fee(money1+""); //单位分所以成100
unifiedOrderRequest.setSpbill_create_ip(ip);//终端IP
unifiedOrderRequest.setNotify_url(Config.getStringValue("notifyUrl"));//通知地址
unifiedOrderRequest.setTrade_type(tradeType);//JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
unifiedOrderRequest.setSign(WeChatPayUtil.createSign(unifiedOrderRequest));//签名
//将订单对象转为xml格式
XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
xStream.alias("xml", UnifiedOrderRequest.class);//根元素名需要是xml
resule = xStream.toXML(unifiedOrderRequest);
System.out.println("XML="+resule);
} catch (Exception e) {
log.info("=.=创建订单异常"+e.getMessage());
e.printStackTrace();
}
log.info("=.=生成订单结束=.=");
return resule;
}
/**
* 统一下单
* @param orderInfo 订单信息
* @return
*/
public static UnifiedOrderRespose httpOrder(String orderInfo) {
try {
log.info("=.=同意下单开始=.=");
HttpURLConnection conn = (HttpURLConnection) new URL(PAYURL).openConnection();
//加入数据
conn.setRequestMethod("POST");
conn.setDoOutput(true);
BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(orderInfo.getBytes());
buffOutStr.flush();
buffOutStr.close();
//获取输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
//将请求返回的内容通过xStream转换为UnifiedOrderRespose对象
xStream.alias("xml", UnifiedOrderRespose.class);
UnifiedOrderRespose unifiedOrderRespose = (UnifiedOrderRespose) xStream.fromXML(sb.toString());
//根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
if(null!=unifiedOrderRespose && "SUCCESS".equals(unifiedOrderRespose.getReturn_code()) && "SUCCESS".equals(unifiedOrderRespose.getResult_code())){
log.info("=.=同意下单成功=.=");
return unifiedOrderRespose;
}else{
log.info("=.=调用微信参数错误=.="+unifiedOrderRespose.getReturn_msg()+"code"+unifiedOrderRespose.getReturn_code());
return null;
}
} catch (Exception e) {
log.info("=.=调用统一下单接口异常"+e.getLocalizedMessage());
e.printStackTrace();
}
return null;
}
/**
* 创建查询订单
* @param outTradeNo
* @return
*/
public String createOrderSelect(String outTradeNo){
//String ip="";
//生成订单对象
String resule="";
try {
String appid=Config.getStringValue("appID");
String mchId=Config.getStringValue("mch_id");
UnifiedOrderSelect unifiedOrderRequest = new UnifiedOrderSelect();
unifiedOrderRequest.setAppid(appid);//公众账号ID
unifiedOrderRequest.setMch_id(mchId);//商户号
unifiedOrderRequest.setNonce_str(WeChatPayUtil.getRandomString(11));//随机字符串
unifiedOrderRequest.setOut_trade_no(outTradeNo);//商户订单号
unifiedOrderRequest.setSign(WeChatPayUtil.createSign(unifiedOrderRequest));//签名
//将订单对象转为xml格式
XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
xStream.alias("xml", UnifiedOrderRequest.class);//根元素名需要是xml
resule = xStream.toXML(unifiedOrderRequest);
System.out.println("XML="+resule);
} catch (Exception e) {
log.info("=.=创建订单异常"+e.getMessage());
e.printStackTrace();
}
log.info("=.=生成订单结束=.=");
return resule;
}
/**
* 请求查询订单
* @param orderInfo
*/
public static UnifiedOrderSelectRespose httpSelectOrder(String orderInfo){
try {
log.info("=.=查询订单开始=.=");
HttpURLConnection conn = (HttpURLConnection) new URL(SELECT).openConnection();
log.info("SELECT"+SELECT);
//加入数据
conn.setRequestMethod("POST");
conn.setDoOutput(true);
BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(orderInfo.getBytes());
buffOutStr.flush();
buffOutStr.close();
//获取输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
//将请求返回的内容通过xStream转换为UnifiedOrderRespose对象
xStream.alias("xml", UnifiedOrderSelectRespose.class);
log.info("xml="+sb.toString());
UnifiedOrderSelectRespose unifiedOrderSelectRespose = (UnifiedOrderSelectRespose) xStream.fromXML(sb.toString());
//根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
if(null!=unifiedOrderSelectRespose && "SUCCESS".equals(unifiedOrderSelectRespose.getReturn_code()) && "SUCCESS".equals(unifiedOrderSelectRespose.getResult_code())){
log.info("=.=查询订单成功=.=");
return unifiedOrderSelectRespose;
}else{
log.info("=.=调用微信参数错误=.="+unifiedOrderSelectRespose.getReturn_msg()+"code"+unifiedOrderSelectRespose.getReturn_code());
return null;
}
} catch (Exception e) {
log.info("=.=调用统一下单接口异常"+e.getLocalizedMessage());
e.printStackTrace();
}
return null;
}
}
创建订单
createOrderInfo(String body,String orderId,float money,String key,String tradeType,String openid)
这个方法就是创建订单,等同于拼XML只不过我这里是封装成对象 然后利用XStream讲对象转成XML
其中有一个OpenId 这个是为了便于以后 微信号内部支付的暂时用不到,所以穿null,这里
商品详情 会显示在用户扫码后提示给用户 你购买的是什么
orderId必须是唯一的,不然同样的订单号无法生成
money是商品价格单位分
key 这里是我的一个参数,我利用WebSocket即使修改页面 告诉用户支付完成
tradeType 类型 SAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 这里我们传NATIVE
微信签名
这个方法没什么说的,无非是set set set 其中unifiedOrderRequest.setSign(WeChatPayUtil.createSign(unifiedOrderRequest))
微信的签名是一个比较重要的东西
这里可以看我微信工具类的代码
具体流程如下
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
- ◆ 参数名ASCII码从小到大排序(字典序);
- ◆ 如果参数的值为空不参与签名;
- ◆ 参数名区分大小写;
- ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
说白了 就是把你所有参数 按照字典序 排序一下 然后拼上key 然后MD5运算一下得到一个字符串
如果签名总是错误可以利用 签名验证工具
创建订单后 就该提交订单了调用UnifiedOrderRespose(Stirng info) 方法
调用同意下单接口
讲上一步拼接后的XML提交给UnifiedOrderRespose
UnifiedOrderRespose 也没什么说的这里我把接收到的值也转换成了对象
/**
* 统一下单返回参数
* @author An
*
*/
public class UnifiedOrderRespose {
private String return_code; //返回状态码
private String return_msg; //返回信息
/* 以下字段在return_code为SUCCESS的时候有返回*/
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; //错误代码描述
/* 以下字段在return_code 和result_code都为SUCCESS的时候有返回 */
private String trade_type; //交易类型
private String prepay_id; //预支付交易会话标识
private String code_url; //二维码链接
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getPrepay_id() {
return prepay_id;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}
public String getCode_url() {
return code_url;
}
public void setCode_url(String code_url) {
this.code_url = code_url;
}
}
这里的 code_url 就是二维码链接 你可以用你的方式转换成二维码,如果没有好的解决方案,可以参考我的其他博客 将URL利用JS 转换成二维码 填充DIV
注意:prepay_id这个参数是微信内部支付需要的东西
接收支付信息
这样二维码就弹出来了,客户开始扫码 如果扫码支付成功,微信就会调用我们的通知接口
//用于回信回调更改支付状态
@RequestMapping("/weChatUpdRecharge")
public void weChatUpdRecharge(HttpServletRequest request,HttpServletResponse response){
log.info("=.=微信异步通知开始=.=");
// 获取post传输的数据
String request_xml = "";
UnifiedOrderCallback unifiedOrderCallback=null;
try {
ServletInputStream inputStram = request.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(
inputStram));
String temp = "";
while ((temp = br.readLine()) != null) {
request_xml = request_xml + temp;
}
log.info("=.=微信返回结果=.=" + request_xml);
XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
//将请求返回的内容通过xStream转换为UnifiedOrderRespose对象
xStream.alias("xml", UnifiedOrderCallback.class);
unifiedOrderCallback = (UnifiedOrderCallback) xStream.fromXML(request_xml);
WechatCallback wechatCallback=new WechatCallback();
wechatCallback.setId(0);
wechatCallback.setBankType(unifiedOrderCallback.getBank_type());
wechatCallback.setErrCode(unifiedOrderCallback.getErr_code());
wechatCallback.setErrCodeDes(unifiedOrderCallback.getErr_code_des());
wechatCallback.setOpenid(unifiedOrderCallback.getOpenid());
wechatCallback.setOutTradeNo(unifiedOrderCallback.getOut_trade_no());
wechatCallback.setRechargeNumber(unifiedOrderCallback.getAttach().split(",")[0]);
wechatCallback.setResultCode(unifiedOrderCallback.getResult_code());
wechatCallback.setTimeEnd(unifiedOrderCallback.getTime_end());
wechatCallback.setTotalFee(Integer.parseInt(unifiedOrderCallback.getTotal_fee()));
wechatCallback.setTransactionId(unifiedOrderCallback.getTransaction_id());
log.info("=.=记录交易记录=.=");
wechatCallbackMapper.insert(wechatCallback);
log.info("=.=修改订单信息=.=");
log.info("=.=判断该订单是否被手动支付完成=.=");
Recharge recharge= rechargeService.selRechargeByNum(wechatCallback.getRechargeNumber());
if(recharge.getStatus()==0){
rechargeService.updRechargeSuccess(wechatCallback.getRechargeNumber(),unifiedOrderCallback.getTime_end());
}else{
log.info("=.=该订单已被手动完成=.=订单号:"+wechatCallback.getRechargeNumber());
}
//通知webSocket支付成功,刷新 页面通知用户您已支付成功
log.info("=.=开始通知页面刷新支付已成功=.=");
String websocketKey=unifiedOrderCallback.getAttach().split(",")[1];
Session session=(Session) ChatServer.map.get(websocketKey);
System.out.println("session==========="+session);
session.getAsyncRemote().sendText("=.=");
ChatServer.map.remove(websocketKey);
} catch (Exception e) {
log.info("=.=异常了=.="+e.getMessage());
} finally{
log.info("=.=告诉微信,我已经知道了=.=");
try {
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
这样一套流程就走完了
public class UnifiedOrderCallback {
private String appid;//应用ID
private String mch_id;//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 openid;//用户标识
private String is_subscribe;//是否关注公众账号
private String trade_type;//交易类型
private String bank_type;//付款银行
private String total_fee;//总金额
private String fee_type;//货币种类
private String cash_fee;//现金支付金额
private String cash_fee_type;//现金支付货币类型
private String coupon_fee;//代金券金额
private String coupon_count;//代金券使用数量
private String coupon_id_$n;//代金券ID
private String coupon_fee_$n;//单个代金券支付金额
private String transaction_id;//微信支付订单号
private String out_trade_no;//商户订单号
private String attach;//商家数据包
private String time_end;//支付完成时间
private String return_code;//返回状态码
private String return_msg;//返回信息
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getCash_fee_type() {
return cash_fee_type;
}
public void setCash_fee_type(String cash_fee_type) {
this.cash_fee_type = cash_fee_type;
}
public String getCoupon_fee() {
return coupon_fee;
}
public void setCoupon_fee(String coupon_fee) {
this.coupon_fee = coupon_fee;
}
public String getCoupon_count() {
return coupon_count;
}
public void setCoupon_count(String coupon_count) {
this.coupon_count = coupon_count;
}
public String getCoupon_id_$n() {
return coupon_id_$n;
}
public void setCoupon_id_$n(String coupon_id_$n) {
this.coupon_id_$n = coupon_id_$n;
}
public String getCoupon_fee_$n() {
return coupon_fee_$n;
}
public void setCoupon_fee_$n(String coupon_fee_$n) {
this.coupon_fee_$n = coupon_fee_$n;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public String getCash_fee() {
return cash_fee;
}
public void setCash_fee(String cash_fee) {
this.cash_fee = cash_fee;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getIs_subscribe() {
return is_subscribe;
}
public void setIs_subscribe(String is_subscribe) {
this.is_subscribe = is_subscribe;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getTime_end() {
return time_end;
}
public void setTime_end(String time_end) {
this.time_end = time_end;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
}