你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
友情提醒:源码购买后不支持退换货
* * @author 叶云轩 at tdg_yyx@foxmail.com * @date 2020/5/7 5:34 下午 */ @Slf4j @Data @NoArgsConstructor @AllArgsConstructor public class UnifiedOrderRequest { /** * 微信支付分配的公众账号ID(企业号corpid即为此appId) */ private String appid; /** * 微信支付分配的商户号 */ private String mch_id; /** * 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB" */ private String device_info; /** * 随机字符串,长度要求在32位以内。 */ private String nonce_str; /** * 通过签名算法计算得出的签名值 */ private String sign; /** * 签名类型,默认为MD5,支持HMAC-SHA256和MD5 */ private String sign_type; /** * 商品简单描述,该字段请按照规范传递 */ private String body; /** * 商品详细描述,对于使用单品优惠的商户,该字段必须按照规范上传 */ private String detail; /** * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 */ private String attach; /** * 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一 */ private String out_trade_no; /** * 符合ISO 4217标准的三位字母代码,默认人民币:CNY */ private String fee_type; /** * 订单总金额,单位为分 */ private Long total_fee; /** * 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP */ private String spbill_create_ip; /** * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010 */ private String time_start; /** * 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。 * 订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期, * 所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则 *
* time_expire只能第一次下单传值,不允许二次修改,二次修改系统将报错。如用户支付失败后,需再次支付,需更换原订单号重新下单。 * 建议:最短失效时间间隔大于1分钟 */ private String time_expire; /** * 订单优惠标记,使用代金券或立减优惠功能时需要的参数,WXG */ private String goods_tag; /** * 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 */ private String notify_url; /** * JSAPI -JSAPI支付 *
* NATIVE -Native支付 *
* APP -APP支付 */ private String trade_type; /** * trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。 */ private String product_id; /** * 上传此参数no_credit--可限制用户不能使用信用卡支付 */ private String limit_pay; /** * trade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。 * openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换 */ private String openid; /** * Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效 */ private String receipt; /** * 使用 md5 签名 * * @param key key */ public void sign(String key) { this.sign(key, 0); } /** * 签名算法 * * @param key 商户 key * @param type 0 : MD5签名 1 : HMAC-SHA256签名 * @return 签名 */ public void sign(String key, int type) { String orderStr = this.encapsulationUnifiedOrderParam(); // 拼接商户 Key String stringSignTemp = (orderStr + "key=" + key); String sign = null; // 签名 if (type == 0) { sign = DigestUtil.md5Hex(stringSignTemp).toUpperCase(); } else if (type == 1) { // todo 未测试 sign = DigestUtil.sha256Hex(stringSignTemp).toUpperCase(); } this.sign = sign; } /** * 封装统一下单参数的方法 * 1. 根据字段名称 ascii 排序 * 2. log 与 sign 不参与排序 * 3. 拼接成特定的字符串结构 * * @return String */ @SneakyThrows public String encapsulationUnifiedOrderParam() { // 获取当前类对象 Class extends UnifiedOrderRequest> thisClass = this.getClass(); // 获取字段信息 Field[] declaredFields = thisClass.getDeclaredFields(); // 字段按照字段名 ascii 排序 Object[] objects = Arrays.stream(declaredFields).map(Field::getName).sorted().toArray(); // 定义一个 StringBuffer 用来接收排序结果 final StringBuffer stringBuffer = new StringBuffer(); for (Object object : objects) { // 字段名称 String filedName = String.valueOf(object); if ("sign".equals(filedName) || "log".equals(filedName)) { // sign log 不参与签名 continue; } Field declaredField = thisClass.getDeclaredField(filedName); // 获取属性的类型 Class> type = declaredField.getType(); String typeName = type.getName(); // 此类中只有 String 和 Long 类型数据 long 类型用于金额 if (String.class.getName().equals(typeName)) { // 字符串类型的数据 Object o = declaredField.get(this); if (o == null) { continue; } String s = String.valueOf(o); stringBuffer.append(filedName).append("=").append(s).append("&"); } else if (Long.class.getName().equals(typeName)) { Object o = declaredField.get(this); if (o == null) { continue; } Long integer = Long.valueOf(String.valueOf(o)); stringBuffer.append(filedName).append("=").append(integer).append("&"); } log.info("[toString] -> [type] {} {}", typeName, filedName); } // 由于排序完还需要拼接key 所以此处不处理最后一个 & return stringBuffer.toString(); } } ``` ### 2. 统一下单 ```java /** * 统一下单的接口 * * @param unifiedOrderRequest 微信统一下单参数 * @return 下单结果 */ @Override public UnifiedOrderResult unifiedOrder(UnifiedOrderRequest unifiedOrderRequest) throws DocumentException { WxPayProperties wx = payProperties.getWx(); String apiSecret = wx.getKey(); // 签名 unifiedOrderRequest.sign(apiSecret); // 组装 xml 参数 String xmlStr = UtilJsonAndXml.jsonToXml(JSONObject.toJSONString(unifiedOrderRequest)); log.info("[unifiedOrder] -> [统一下单参数] {}", xmlStr); // 调用统一下单 String post = HttpUtil.post(wx.getUnifiedOrder(), xmlStr); // TODO 这里应该做一个容错处理。因为是 DEMO 所以默认认为成功 String toJson = UtilJsonAndXml.xmlToJson(post); log.info("[unifiedOrder] -> [toJson] {} -> {}", post, toJson); // JSON 转对象 UnifiedOrderResponse unifiedOrderResponse = JSONObject.parseObject(toJson, UnifiedOrderResponse.class); UnifiedOrderXmlResponse unifiedOrderXmlResponseObj = unifiedOrderResponse.getXml(); UnifiedOrderResult unifiedOrderResult = new UnifiedOrderResult(); // 判断是否通信成功 if (unifiedOrderXmlResponseObj.communicationResult()) { // 通信成功 判断是否业务参数成功 if (unifiedOrderXmlResponseObj.businessResult()) { // 下单成功 生成二维码图片,转base64 String codeUrl = unifiedOrderXmlResponseObj.getCode_url(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); QrCodeUtil.generate(codeUrl, 240, 240, ImgUtil.IMAGE_TYPE_PNG, byteArrayOutputStream); byte[] bytes = byteArrayOutputStream.toByteArray(); String base64QrCode = "data:image/jpg;base64," + Base64.encode(bytes); unifiedOrderResult.successResult(base64QrCode); } else { unifiedOrderResult.errorResult(unifiedOrderXmlResponseObj.getErr_code_des()); } } else { unifiedOrderResult.errorResult(unifiedOrderXmlResponseObj.getReturn_msg()); } return unifiedOrderResult; } ``` ### 3. 支付回调 > 由于是 DEMO。所以这里只做了一个正确返回 > > 且这里是使用了Callable来返回,实际上这里应该是使用 MQ 来异步处理 ```java @Override public Callable wxPayCallBack(HttpServletRequest httpServletRequest) { WxReturn wxReturn = new WxReturn(); ServletInputStream inputStream = null; try { inputStream = httpServletRequest.getInputStream(); } catch (IOException e) { log.error("[wxPayCallBack] -> [从请求流中获取信息失败]", e); wxReturn.errorResult(e.getMessage()); } InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); StringBuffer stringBuffer = new StringBuffer(); String str = null; while (true) { try { if ((str = bufferedReader.readLine()) == null) { break; } } catch (IOException e) { log.error("[wxPayCallBack] -> [IO 异常]", e); wxReturn.errorResult(e.getMessage()); } stringBuffer.append(str); } // 从请求中获取微信返回的数据 String wxResponseXmlStr = stringBuffer.toString(); log.info("[wxCallBack] -> [wxResponseXmlStr] {}", wxResponseXmlStr); return () -> { // 微信支付回调内容转 JSON 数据 String wxPayResultJsonStr = UtilJsonAndXml.xmlToJson(wxResponseXmlStr); UnifiedOrderPayResultNotifyParam unifiedOrderPayResultNotifyParam = JSONObject.parseObject(wxPayResultJsonStr, UnifiedOrderPayResultNotifyParam.class); boolean communicationResult = unifiedOrderPayResultNotifyParam.communicationResult(); if (communicationResult) { // 执行业务逻辑 boolean b = payBusinessService.dealPayResult(unifiedOrderPayResultNotifyParam); if (b) { wxReturn.successResult(); } else { wxReturn.errorResult("未知异常"); } } else { wxReturn.errorResult(unifiedOrderPayResultNotifyParam.getReturn_msg()); } return UtilJsonAndXml.jsonToXml(JSONObject.toJSONString(wxReturn)); }; } ``` ## 说明 > 此 DEMO 分为两部分, > > 一部分是 SDK , 一部分是 DEMO 演示。请注意配置文件内容 > > SDK 包是 pay > > DEMO 演示包是 pay-demo 注意, 一定要使用提供的配置字段名。 - pay 内部配置 ```yml # 目前只介入了一种方式,所以只配置这个就可以 pay: wx: # 统一下单 url unifiedOrder: https://api.mch.weixin.qq.com/pay/unifiedorder unifiedOrderBack: https://api2.mch.weixin.qq.com/pay/unifiedorder ``` - pay 外部配置 ```yml pay: wx: notify: 回调接口 URL 例如: http://domain/wx/callback key: 商户配置的 API 密钥 mchId: 商户号 appId: 商户绑定的 APPID # 其余配置见 DEMO 哦 ``` ## 运行说明 1. 将项目引入到 IDEA 中 2. 找到 **pay-demo**项目中的 **application-pay.yml** 配置文件 修改为自己的配置。(我会注明的) 3. 找到**PayDemoApplication**类。启动即可。 4. 默认端口 8084 ## DEMO 运行 ![IDEA运行](/contentImages/image/20200510/eAYJJRRiitmrnnJIt0D.png) ![扫码](/contentImages/image/20200510/8afBOj7Bww6qni8HszE.jpg) ## 源码结构 ![](/contentImages/image/20200511/JcBAIN4kxW1lsW9WWv8.png)
服务描述:帮助环境搭建以及demo的运行和讲解。 远程工具:向日葵(因为TEAM被限制了,帮的人太多,误认为用做企业中了- -)
服务价格:¥10
我要联系