微信公众号支付(二)实现统一下单接口

作者:mrr 时间:2023-01-12 21:29:18 

上一篇已经获取到了用户的OpenId

这篇主要是调用微信公众支付的统一下单API

API地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

看文档,主要流程就是把20个左右的参数封装为XML格式发送到微信给的接口地址,然后就可以获取到返回的内容了,如果成功里面就有支付所需要的预支付ID

请求参数就不解释了。

其中,随机字符串:我用的是UUID去中划线


public static String create_nonce_str() {
return UUID.randomUUID().toString().replace("-","");
}

商户订单号:每个订单号只能使用一次,所以用的是系统的订单号加的时间戳。

总金额:不能为

通知地址:微信支付成功或失败回调给系统的地址

签名:


import java.io.Serializable;
public class PayInfo implements Serializable{
private static final long serialVersionUID = L;
private String appid;
private String mch_id;
private String device_info;
private String nonce_str;
private String sign;
private String body;
private String attach;
private String out_trade_no;
private int total_fee;
private String spbill_create_ip;
private String notify_url;
private String trade_type;
private String openid;
//下面是get,set方法
}

/**
* 创建统一下单的xml的java对象
* @param bizOrder 系统中的业务单号
* @param ip 用户的ip地址
* @param openId 用户的openId
* @return
*/
public PayInfo createPayInfo(BizOrder bizOrder,String ip,String openId) {
 PayInfo payInfo = new PayInfo();
 payInfo.setAppid(Constants.appid);
 payInfo.setDevice_info("WEB");
 payInfo.setMch_id(Constants.mch_id);
 payInfo.setNonce_str(CommonUtil.create_nonce_str().replace("-", ""));
 payInfo.setBody("这里是某某白米饭的body");
 payInfo.setAttach(bizOrder.getId());
 payInfo.setOut_trade_no(bizOrder.getOrderCode().concat("A").concat(DateFormatUtils.format(new Date(), "MMddHHmmss")));
 payInfo.setTotal_fee((int)bizOrder.getFeeAmount());
 payInfo.setSpbill_create_ip(ip);
 payInfo.setNotify_url(Constants.notify_url);
 payInfo.setTrade_type("JSAPI");
 payInfo.setOpenid(openId);
 return payInfo;
}

获取签名:


/**
* 获取签名
* @param payInfo
* @return
* @throws Exception
*/
public String getSign(PayInfo payInfo) throws Exception {
 String signTemp = "appid="+payInfo.getAppid()
  +"&attach="+payInfo.getAttach()
  +"&body="+payInfo.getBody()
  +"&device_info="+payInfo.getDevice_info()
  +"&mch_id="+payInfo.getMch_id()
  +"&nonce_str="+payInfo.getNonce_str()
  +"&notify_url="+payInfo.getNotify_url()
  +"&openid="+payInfo.getOpenid()
  +"&out_trade_no="+payInfo.getOut_trade_no()
  +"&spbill_create_ip="+payInfo.getSpbill_create_ip()
  +"&total_fee="+payInfo.getTotal_fee()
  +"&trade_type="+payInfo.getTrade_type()
  +"&key="+Constants.key; //这个key注意
MessageDigest md = MessageDigest.getInstance("MD");
md.reset();
md.update(signTemp.getBytes("UTF-"));
String sign = CommonUtil.byteToStr(md.digest()).toUpperCase();
return sign;
}

注意:上面的Constants.key取值在商户号API安全的API密钥中。

一些工具方法:获取ip地址,将字节数组转换为十六进制字符串,将字节转换为十六进制字符串


/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
public static String byteToStr(byte[] byteArray) {
 String strDigest = "";
 for (int i = ; i < byteArray.length; i++) {
 strDigest += byteToHexStr(byteArray[i]);
 }
 return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param btyes
* @return
*/
public static String byteToHexStr(byte bytes) {
 char[] Digit = { '', '', '', '', '', '', '', '', '', '', 'A', 'B', 'C', 'D', 'E', 'F' };
 char[] tempArr = new char[];
 tempArr[] = Digit[(bytes >>> ) & XF];
 tempArr[] = Digit[bytes & XF];
 String s = new String(tempArr);
 return s;
}
/**
* 获取ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
 InetAddress addr = null;
 try {
 addr = InetAddress.getLocalHost();
 } catch (UnknownHostException e) {
 return request.getRemoteAddr();
 }
 byte[] ipAddr = addr.getAddress();
 String ipAddrStr = "";
 for (int i = ; i < ipAddr.length; i++) {
 if (i > ) {
  ipAddrStr += ".";
 }
 ipAddrStr += ipAddr[i] & xFF;
 }
 return ipAddrStr;
}

这样就获取了签名,把签名与PayInfo中的其他数据转成XML格式,当做参数传递给统一下单地址。


PayInfo pi = pu.createPayInfo(bo,"...","");
String sign = pu.getSign(pi);
pi.setSign(sign);


对象转XML




/**
* 扩展xstream使其支持CDATA
*/
private static XStream xstream = new XStream(new XppDriver() {
 public HierarchicalStreamWriter createWriter(Writer out) {
 return new PrettyPrintWriter(out) {
  //增加CDATA标记
  boolean cdata = true;
  @SuppressWarnings("rawtypes")
  public void startNode(String name, Class clazz) {
  super.startNode(name, clazz);
  }
  protected void writeText(QuickWriter writer, String text) {
  if (cdata) {
   writer.write("<![CDATA[");
   writer.write(text);
   writer.write("]]>");
  } else {
   writer.write(text);
  }
  }
 };
 }
});
public static String payInfoToXML(PayInfo pi) {
 xstream.alias("xml", pi.getClass());
 return xstream.toXML(pi);
}

xml转Map


@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(String xml) throws Exception {
 Map<String, String> map = new HashMap<String, String>();
 Document document = DocumentHelper.parseText(xml);
 Element root = document.getRootElement();
 List<Element> elementList = root.elements();
 for (Element e : elementList)
 map.put(e.getName(), e.getText());
 return map;
}

下面就是调用统一下单的URL了


log.info(MessageUtil.payInfoToXML(pi).replace("__", "_"));
Map<String, String> map = CommonUtil.httpsRequestToXML("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", MessageUtil.payInfoToXML(pi).replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""));
log.info(map);

public static Map<String, String> httpsRequestToXML(String requestUrl, String requestMethod, String outputStr) {
 Map<String, String> result = new HashMap<>();
 try {
 StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr);
 result = MessageUtil.parseXml(buffer.toString());
 } catch (ConnectException ce) {
 log.error("连接超时:"+ce.getMessage());
 } catch (Exception e) {
 log.error("https请求异常:"+ece.getMessage());
 }
 return result;
}

httpsRequest()这个方法在第一篇中

上面获取到的Map如果成功的话,里面就会有


String return_code = map.get("return_code");
if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")){
String return_msg = map.get("return_msg");
if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {
return "统一下单错误!";
}
}else{
return "统一下单错误!";
}
String prepay_Id = map.get("prepay_id");

这个prepay_id就是预支付的ID。后面支付需要它。

标签:微信支付,下单接口
0
投稿

猜你喜欢

  • Android适配底部虚拟按键的方法详解

    2023-11-09 18:20:55
  • Java基础语法之二维数组详解

    2023-05-09 16:49:20
  • Java数据结构之栈与队列实例详解

    2021-05-29 03:25:13
  • Android编程基于重力传感器实现横竖屏放向切换功能

    2022-11-30 00:38:54
  • Java如何去掉指定字符串的开头的指定字符

    2022-05-23 13:28:31
  • C++中的异常处理机制详解

    2023-04-16 16:01:10
  • C#实战之备忘录的制作详解

    2023-08-13 02:17:21
  • jvm细节探索之synchronized及实现问题分析

    2023-08-24 02:13:29
  • Java webservice的POST和GET请求调用方式

    2023-01-10 05:35:43
  • C#多线程系列之线程池

    2023-02-21 17:25:44
  • Swagger2配置方式(解决404报错)

    2022-08-30 17:21:13
  • mybatis 字段名自动转小写的实现

    2021-09-09 20:04:26
  • SpringBoot教程_创建第一个SpringBoot项目

    2022-02-19 23:12:54
  • 基于Spring概念模型:PathMatcher 路径匹配器

    2022-08-20 12:52:38
  • c#如何用好垃圾回收机制GC

    2023-03-21 08:48:22
  • Android用动画显示或隐藏视图

    2023-08-05 20:07:25
  • Android使用Intent传递组件大数据

    2023-09-30 12:21:46
  • c# 反射+自定义特性保存数据至本地

    2023-03-14 03:07:40
  • springboot实现返回文件流

    2023-04-04 22:19:22
  • Android如何获取APP启动时间

    2021-11-13 06:13:47
  • asp之家 软件编程 m.aspxhome.com