admin
2022-04-16 04f09e52ffd4681bdfd85e51acd3da0d1280c3d3
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
package com.yeshi.buwan.controller;
 
import com.yeshi.buwan.service.inter.order.OrderService;
import com.yeshi.buwan.util.StringUtil;
import com.yeshi.buwan.util.log.LoggerUtil;
import com.yeshi.buwan.util.user.VipUtil;
import com.yeshi.buwan.util.vip.VIPOrderUtil;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.yeshi.utils.entity.wx.WXAPPInfo;
 
import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
 
@Controller
@RequestMapping("wx")
public class WXController {
    Logger logger = LoggerFactory.getLogger(WXController.class);
 
    @Resource
    private OrderService orderService;
 
    private static String decryptToString(String apiV3Key, String associatedData, String nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
 
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes("UTF-8"), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes("UTF-8"));
 
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes("UTF-8"));
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
 
    /**
     * 处理通知结果
     *
     * @param request
     * @throws Exception
     */
    private void process(HttpServletRequest request) throws Exception {
        WXAPPInfo wxappInfo = VipUtil.getWXAPP();
        //验证证书序列号
        String mchSerialNo = request.getHeader("Wechatpay-Serial");
        LoggerUtil.getTestLogger().info("微信支付mchSerialNo:{}",mchSerialNo);
//        if (!mchSerialNo.equalsIgnoreCase(wxappInfo.getMchSerialNo())) {
//            throw new Exception("证书序列号不一致");
//        }
 
        String timeStamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String data = null;
 
        try {
            if (request.getInputStream() != null) {
                String entity = IOUtils.toString(request.getInputStream(), "UTF-8");
                data = entity;
                LoggerUtil.getTestLogger().info("微信支付回调entity:{}",entity);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
        if (StringUtil.isNullOrEmpty(data)) {
            throw new Exception("通知的内容为空");
        }
 
        JSONObject resultJson = JSONObject.fromObject(data);
        String eventType = resultJson.optString("event_type");
        switch (eventType) {
            //支付成功
            case "TRANSACTION.SUCCESS":
                JSONObject resource = resultJson.optJSONObject("resource");
                String ciphertext = resource.optString("ciphertext");
                String r = decryptToString(wxappInfo.getApiV3Key(), resource.optString("associated_data"), resource.optString("nonce"), ciphertext);
                //解密格式如下 {"mchid":"1520950211","appid":"wxa99686bb65a9f466","out_trade_no":"buwan-vip-8","transaction_id":"4200000796202101259681241680","trade_type":"MWEB","trade_state":"SUCCESS","trade_state_desc":"支付成功","bank_type":"OTHERS","attach":"","success_time":"2021-01-25T16:18:33+08:00","payer":{"openid":"oq7R20lxhKF8qSnkszxFJHViyKEY"},"amount":{"total":10,"payer_total":10,"currency":"CNY","payer_currency":"CNY"}}
                LoggerUtil.getTestLogger().info("数据解码:{}",r);
                JSONObject decript = JSONObject.fromObject(r);
                String outTradeNo = decript.optString("out_trade_no");
                String appId = decript.optString("appid");
                String mchId = decript.optString("mchid");
                String tradeState = decript.optString("trade_state");
                //支付成功
                if (tradeState.equalsIgnoreCase("SUCCESS")) {
                    String id = VIPOrderUtil.getIdFromOutOrderNo(outTradeNo);
                    orderService.checkOrderPayState(id);
                }
                break;
        }
    }
 
 
    /**
     * 微信支付结果通知
     *
     * @param request
     * @param response
     */
    @RequestMapping("pay/vip")
    public void vipPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            process(request);
            JSONObject data = new JSONObject();
            data.put("code", "SUCCESS");
            data.put("message", "处理成功");
            response.sendError(200, data.toString());
        } catch (Exception e) {
            logger.error("微信支付回调处理出错:",e);
            e.printStackTrace();
            JSONObject data = new JSONObject();
            data.put("code", "FAIL");
            data.put("message", e.getMessage());
            response.sendError(500, data.toString());
        }
 
    }
 
    public static void main(String[] args) {
 
        String result = "{\n" +
                "    \"id\":\"c86a0485-0b56-5c6a-b449-d2a17fbb98ea\",\n" +
                "    \"create_time\":\"2021-01-25T16:18:33+08:00\",\n" +
                "    \"resource_type\":\"encrypt-resource\",\n" +
                "    \"event_type\":\"TRANSACTION.SUCCESS\",\n" +
                "    \"summary\":\"支付成功\",\n" +
                "    \"resource\":{\n" +
                "        \"original_type\":\"transaction\",\n" +
                "        \"algorithm\":\"AEAD_AES_256_GCM\",\n" +
                "        \"ciphertext\":\"UG6kFmJ4z+zCyOldYZLa/y+jzWwyLLMjc8usFqum0Yh08qR0nGPhLYyXCyIGi6DvnG33Ju7t1grKBA5VP0nLqw8NrITwajCK+Ph6avkxSz6lv7ObenIGp6tg+SJe/UxS9rToLH0sDVUB4L6XvawHw5ite/40G0lrdVCbZlJZCm4NxfnH3iRMnBZBw3QaUlySRoKugMIc2WzloHueq7K4bc7tCln4uro3eNDEHe++p2QWuiHmDm6s+aMLHKdQj723aVPhmZLc1kWqUAua6QJx77Jn3eYy8I0PksTKh1EdK3DV4+Fl4+UBZ86aqHdIHy+7gj0vRyDXXIUdfHYHQPMkYr46KfjoVSbA/Jop+2uH610a8jBRP4aP/3YJ2XBJbvw/DSupTJ7z8k1e9xCNJUfN3SeycvFIyaQI/mYi8jpe2F6ek2wexGUAXB5bDmoNGMGkIbbLE9NjJazF8O1WtQilqMIeJ/CiW6Pnrn0N/BibK1EuJxyr2dXmHt+aA+jpvwI8pTWBXn8ZeYG9CeMM0Ml+mBQcPzYdPx1ZsrKylJoIlvwSZTy4cRGSQlC/0w==\",\n" +
                "        \"associated_data\":\"transaction\",\n" +
                "        \"nonce\":\"2pIZSvqoRYYu\"\n" +
                "    }\n" +
                "}";
        WXAPPInfo wxappInfo = new WXAPPInfo("wxa99686bb65a9f466", "1520950211", "454328C324C6CC21355D064B44D6524CD7506DD0", "", "XYJkJ2018FAfaodCCx899mLl138rfGVd");
 
        JSONObject resultJson = JSONObject.fromObject(result);
        JSONObject resource = resultJson.optJSONObject("resource");
        String ciphertext = resource.optString("ciphertext");
        try {
            String r = decryptToString(wxappInfo.getApiV3Key(), resource.optString("associated_data"), resource.optString("nonce"), ciphertext);
            System.out.println(r);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
 
 
}