微信支付wxpay -- 移动端app第二章节 -- java 后端代码

一共需要5处

1.pom,添加如下依赖

1
2
3
4
5
<dependency>
   <groupId>com.github.wxpaygroupId>
   <artifactId>wxpay-sdkartifactId>
   <version>0.0.3version>
dependency>

2.配置文件(文件夹):

WxpayConfig.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#微信支付配置
#移动端appid
APP_ID = wx3d3c125230d2ba**
 
#商户号
BUSINESS_CODE = 153982***
 
#秘钥--sign获取需要的,在  账户中心-->api安全--api秘钥设置
API_KEY = gin201907***********
 
APIV3_KEY = gin2*******
 
#签名类型   HMAC-SHA256和MD5,默认为MD5
SIGN_TYPE = MD5
 
#证书地址
PEM_ADDRESS = /wxConfig/**/apiclient_cert.p12
 
#异步通知地址(请注意必须是外网)
NOTIFY_URL =http://***/wxAppPay/notify
 
 
#公众号的appID --待确定---
#GZH_APP_ID = wxcfe45ec****

同时可以再同目录文件夹放入证书,微信支付部分功能需要证书

3. 生成sign的util类:WxMD5Util

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
 
/**
 * Created by Mafy on 2019/7/9.
 * 该类是生成签名用
 */
public class WxMD5Util {
 
    public static String getSign(Map data) throws Exception {
        WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
        Set keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(config.getKey());
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        byte[] array = new byte[0];
        try {
            array = md.digest(sb.toString().getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        StringBuilder sb2 = new StringBuilder();
        for (byte item : array) {
            sb2.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb2.toString().toUpperCase();
    }
}

4.获取配置文件的配置类--该处使用单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.util.ObjectUtils;
 
import java.io.*;
import java.util.Properties;
 
/**
 * Created by Mafy on 2019/7/9.
 */
public class WXConfigUtil implements WXPayConfig{
    Properties properties= null;
    private byte[] certData;
 
    private static WXConfigUtil wxConfigUtil = new WXConfigUtil();
 
    public static WXConfigUtil initWXconfigUtil(){
        return wxConfigUtil;
    }
 
    private WXConfigUtil() {
        String certPath = PropertiesUtil.class.getResource(getProperties().getProperty("PEM_ADDRESS")).getPath();//从微信商户平台下载的安全证书存放的路径
        InputStream certStream = null;
        try {
            File file = new File(certPath);
            certStream = new FileInputStream(file);
            this.certData = new byte[(int) file.length()];
            certStream.read(this.certData);
 
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(!ObjectUtils.isEmpty(certStream)){
                try {
                    certStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    @Override
    public String getAppID() {
        return getProperties().getProperty("APP_ID");
    }
 
    //parnerid,商户号
    @Override
    public String getMchID() {
        return getProperties().getProperty("BUSINESS_CODE");
    }
 
    @Override
    public String getKey() {
        return getProperties().getProperty("API_KEY");
    }
 
    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }
 
    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }
 
    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }
 
    Properties getProperties() {
        if(null == properties) {
            properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties");
        }
        return properties;
    }
}

5.调用支付接口的controller:WxAppPayController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.xinlianpu.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
 
/**
 * Created by Mafy on 2019/7/9.
 */
@Controller
@ResponseBody
@RequestMapping("/wxAppPay")
public class WxAppPayController {
 
    static Properties properties= null;
 
    final static Logger logger = LoggerFactory.getLogger(WxAppPayController.class);
 
//    @Resource
//    private WxPayService wxPayService;
 
    //异步通知地址(请注意必须是外网)
    public static final String NOTIFY_URL = getProperties().getProperty("NOTIFY_URL");
    //交易类型,JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付
    public static final String TRADE_TYPE_APP = "APP";
 
 
    /**
     * 统一下单--不需要证书
     * 包括生成预支付订单-->订单生成-->返回订单数据到前端
     * 官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     * @param jsonObject
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/pay",method = RequestMethod.POST)
//    public Map wxPay(@RequestParam(value = "userId") String user_id, @RequestParam(value = "totalFee") String total_fee) throws Exception {
    public DataResponse wxPayUnEncryption(@RequestBody JSONObject jsonObject) throws Exception {
        logger.info("下单支付,app传过来的参数:{}",jsonObject);
 
        DataResponse dataResponse = DataResponse.success();
//        String user_id = TokenManager.getUserId();
        String user_id = "123";
//        String total_fee = jsonObject.getString("totalFee");//总金额
        String total_fee = "1";//总金额--分
        String spbillCreateIp = jsonObject.getString("spbillCreateIp");//点击支付按钮的机器的ip
//        String spbillCreateIp = "0.01";//点击支付按钮的机器的ip
 
 
        Map map = null;
        try {
//            String attach = "{\"user_id\":\"" + user_id + "\"}";//扩展字段,原样返回
            String attach = "扩展字段";//扩展字段,原样返回
            //请求预支付订单
            Map result = dounifiedOrder(attach, total_fee,spbillCreateIp);
            logger.info("下单之前,预支付返回信息:{}",JSONObject.toJSON(result));
            //可判断改预支付订单的数据是否正确
            //预支付订单之后,生成带签名的客户端您支付信息
            map = new HashMap<>();
 
            //返回APP端的数据
            //参加调起支付的签名字段有且只能是6个,分别为 appid、partnerid、prepayid、package、noncestr 和 timestamp,而且都必须是小写---start--
            map.put("appid", result.get("appid"));
            map.put("partnerid", result.get("mch_id"));
            map.put("prepayid", result.get("prepay_id"));
            map.put("package", "Sign=WXPay");
            map.put("noncestr", result.get("nonce_str"));
            String signTimstamp = String.valueOf(System.currentTimeMillis() / 1000);
            map.put("timestamp", signTimstamp);//单位为秒
            //参加调起支付的签名字段有且只能是6个,分别为appid、partnerid、prepayid、package、noncestr和timestamp,而且都必须是小写---end--
 
 
            Map mobileParam = new HashMap<>();
            mobileParam.put("appId", result.get("appid"));
            mobileParam.put("partnerId", result.get("mch_id"));
            mobileParam.put("prepayId", result.get("prepay_id"));
            mobileParam.put("packageValue", "Sign=WXPay");//???
            mobileParam.put("nonceStr", result.get("nonce_str"));
            mobileParam.put("timeStamp", signTimstamp);//单位为秒
            //----这里不要使用请求预支付订单时返回的签名-------
            mobileParam.put("sign", WxMD5Util.getSign(map));
            mobileParam.put("extData", attach);
            logger.info("支付返回参数:{}",JSONObject.toJSONString(map));
            logger.info("返回移动端的参数:{}",JSONObject.toJSONString(mobileParam));
            dataResponse.setData(mobileParam);
        } catch (Exception e) {
            e.printStackTrace();
            return DataResponse.failure();
        }
        return dataResponse;
    }
 
 
    /**
     * 微信退款接口--申请退款
     * @param jsonObject
     * @return
     */
    public DataResponse wxRefund(@RequestBody JSONObject jsonObject){
        DataResponse model= DataResponse.success();
        String orderId = jsonObject.getString("orderId");//订单ID
        try {
            WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
            WXPay wxpay = new WXPay(config);
            Map data = new HashMap<>();
 
 
            Map refund = wxpay.refund(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        return model;
    }
 
 
 
    /**
     *   支付异步结果通知,我们在请求预支付订单时传入的地址--无参数
     *   官方文档 :https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3
     */
    @RequestMapping(value = "/notify", method = {RequestMethod.GET, RequestMethod.POST})
    public String wxPayNotifyUnEncryption(HttpServletRequest request, HttpServletResponse response) {
        String resXml = "";
        try {
            InputStream inputStream = request.getInputStream();
            //将InputStream转换成xmlString
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            resXml = sb.toString();
            String result = payBack(resXml);
            return result;
        } catch (Exception e) {
            logger.info("微信手机支付失败:" + e.getMessage());
            System.out.println("微信手机支付失败:" + e.getMessage());
            String result = "" + "" + "" + " ";
            return result;
        }
    }
 
 
    /**
     * 调用官方SDK 获取预支付订单等参数
     *
     * @param attach    额外参数
     * @param total_fee 总价
     * @return
     * @throws Exception
     */
    public Map dounifiedOrder(String attach, String total_fee,String spbillCreateIp) throws Exception {
        logger.info("进入调用官方sdk方法,参数:{},{}",attach,total_fee);
        Map returnMap = new HashMap<>();
        WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
        WXPay wxpay = new WXPay(config);
        Map data = new HashMap<>();
        //生成商户订单号,不可重复
        String out_trade_no = "wxpay" + System.currentTimeMillis();
 
        data.put("appid", config.getAppID());//应用ID
        data.put("mch_id", config.getMchID());//商户号
        data.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串,不长于32位。推荐随机数生成算法
        String body = "新脸谱微信订单支付-测试-"+ System.currentTimeMillis();
        data.put("body", body); //商品描述
        data.put("out_trade_no", out_trade_no); //商户订单号
        data.put("total_fee", total_fee); //总金额
        data.put("spbill_create_ip", spbillCreateIp);//自己的服务器IP地址 ---点击支付的机器IP
        data.put("notify_url", NOTIFY_URL);//异步通知地址(请注意必须是外网)
        data.put("trade_type", TRADE_TYPE_APP);//交易类型
        data.put("attach", attach);//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
        String sign1 = WxMD5Util.getSign(data);
        data.put("sign", sign1); //签名
        logger.info("生成预订单的参数,{}", JSONObject.toJSON(data));
        try {
            //使用官方API请求预付订单
            Map response = wxpay.unifiedOrder(data);
            logger.info("预订单的返回结果,{}", JSONObject.toJSON(response));
            String returnCode = response.get("return_code");    //获取返回码
            //若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
            if (returnCode.equals("SUCCESS")) {//主要返回以下5个参数
                logger.info("预订单的返回结果SUCCESS,{}", JSONObject.toJSON(response));
                String resultCode = response.get("result_code");
                returnMap.put("appid", response.get("appid"));
                returnMap.put("mch_id", response.get("mch_id"));
                returnMap.put("nonce_str", response.get("nonce_str"));
                returnMap.put("sign", response.get("sign"));
                if ("SUCCESS".equals(resultCode)) {//resultCode 为SUCCESS,才会返回prepay_id和trade_type
                    //获取预支付交易回话标志
                    returnMap.put("trade_type", response.get("trade_type"));
                    returnMap.put("prepay_id", response.get("prepay_id"));
                    return returnMap;
                } else {
                    logger.info("预订单的返回结果ERROR,{}", JSONObject.toJSON(response));
                    //此时返回没有预付订单的数据
                    return returnMap;
                }
            } else {
                return returnMap;
            }
        } catch (Exception e) {
            System.out.println(e);
            //系统等其他错误的时候
        }
        return returnMap;
    }
 
 
    /**
     * @param notifyData 异步通知后的XML数据
     * @return
     */
    public String payBack(String notifyData) {
        logger.info("异步通知进入方法数据:{}",notifyData);
        WXConfigUtil config = null;
        try {
            config = WXConfigUtil.initWXconfigUtil();
        } catch (Exception e) {
            e.printStackTrace();
        }
        WXPay wxpay = new WXPay(config);
        String xmlBack = "";
        Map notifyMap = null;
        try {
            notifyMap = WXPayUtil.xmlToMap(notifyData);         // 调用官方SDK转换成map类型数据
            logger.info("支付异步结果通知:{}",JSONObject.toJSONString(notifyMap));
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {//验证签名是否有效,有效则进一步处理
 
                String return_code = notifyMap.get("return_code");//状态
                String out_trade_no = notifyMap.get("out_trade_no");//商户订单号
                if (return_code.equals("SUCCESS")) {
                    if (out_trade_no != null) {
                        // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户的订单状态从退款改成支付成功
                        // 注意特殊情况:微信服务端同样的通知可能会多次发送给商户系统,所以数据持久化之前需要检查是否已经处理过了,处理了直接返回成功标志
                        //业务数据持久化
 
                        System.err.println("支付成功");
 
                        logger.info("微信手机支付回调成功订单号:{}", out_trade_no);
                        xmlBack = "" + "" + "" + " ";
                    } else {
                        logger.info("微信手机支付回调失败订单号:{}", out_trade_no);
                        xmlBack = "" + "" + "" + " ";
                    }
                }
                return xmlBack;
            } else {
                // 签名错误,如果数据里没有sign字段,也认为是签名错误
                //失败的数据要不要存储?
                logger.error("手机支付回调通知签名错误");
                xmlBack = "" + "" + "" + " ";
                return xmlBack;
            }
        } catch (Exception e) {
            logger.error("手机支付回调通知失败", e);
            xmlBack = "" + "" + "" + " ";
        }
        return xmlBack;
    }
 
    static Properties getProperties() {
        if(null == properties) {
            properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties");
        }
        return properties;
    }

  

 

 

 

 

 

 

posted @   天妖姥爷  阅读(996)  评论(0编辑  收藏  举报
编辑推荐:
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
阅读排行:
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· C# 从零开始使用Layui.Wpf库开发WPF客户端
· C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)
· 接口重试的7种常用方案!
点击右上角即可分享
微信分享提示